home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / xboard21.lha / xboard-2.1.pl11 / xboard.c < prev    next >
C/C++ Source or Header  |  1993-06-14  |  172KB  |  6,031 lines

  1. /*
  2.  * XBoard -- an Xt/Athena user interface for GNU Chess
  3.  *
  4.  * Original authors:  Dan Sears and Chris Sears.
  5.  * Enhancements (Version 2.0 and following):  Tim Mann.
  6.  * Thanks to John Chanak for the initial implementation of ICS mode.
  7.  *
  8.  * XBoard borrows its colors, icon and piece bitmaps from XChess
  9.  * which was written and is copyrighted by Wayne Christopher.
  10.  *
  11.  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
  12.  * Enhancements Copyright 1992 Free Software Foundation, Inc.
  13.  *
  14.  * The following terms apply to Digital Equipment Corporation's copyright
  15.  * interest in XBoard:
  16.  * ------------------------------------------------------------------------
  17.  * All Rights Reserved
  18.  *
  19.  * Permission to use, copy, modify, and distribute this software and its
  20.  * documentation for any purpose and without fee is hereby granted,
  21.  * provided that the above copyright notice appear in all copies and that
  22.  * both that copyright notice and this permission notice appear in
  23.  * supporting documentation, and that the name of Digital not be
  24.  * used in advertising or publicity pertaining to distribution of the
  25.  * software without specific, written prior permission.
  26.  *
  27.  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  28.  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
  29.  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  30.  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  31.  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  32.  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  33.  * SOFTWARE.
  34.  * ------------------------------------------------------------------------
  35.  *
  36.  * This file is part of XBOARD.
  37.  *
  38.  * XBOARD is distributed in the hope that it will be useful, but WITHOUT ANY
  39.  * WARRANTY.  No author or distributor accepts responsibility to anyone for
  40.  * the consequences of using it or for whether it serves any particular
  41.  * purpose or works at all, unless he says so in writing.  Refer to the XBOARD
  42.  * General Public License for full details.
  43.  *
  44.  * Everyone is granted permission to copy, modify and redistribute XBOARD, but
  45.  * only under the conditions described in the XBOARD General Public License. A
  46.  * copy of this license is supposed to have been given to you along with
  47.  * XBOARD so you can know your rights and responsibilities.  It should be in a
  48.  * file named COPYING.  Among other things, the copyright notice and this
  49.  * notice must be preserved on all copies.
  50.  * ------------------------------------------------------------------------
  51.  *
  52.  * See the file ChangeLog for a detailed revision history.
  53.  */
  54.  
  55. #define VERSION "2.1"
  56.  
  57. #include <stdio.h>
  58. #include <ctype.h>
  59. #include <signal.h>
  60. #include <errno.h>
  61. #include <sys/ioctl.h>
  62. #include <sys/types.h>
  63. #include <sys/socket.h>
  64. #include <netinet/in.h>
  65. #include <netdb.h>
  66. #include <time.h>
  67. #ifdef HAS_GETTIMEOFDAY
  68. #ifndef ESIX
  69. #include <sys/time.h>
  70. #endif
  71. #endif
  72. #ifdef    __STDC__
  73. #ifndef ESIX
  74. #include <stdlib.h>
  75. #endif
  76. #endif
  77. #if defined(SYSTEM_FIVE) || defined(SYSV)
  78. #include <sys/types.h>
  79. #include <sys/stat.h>
  80. #ifdef AIXV3
  81. #include <fcntl.h>
  82. #else
  83. #include <sys/fcntl.h>
  84. #endif /*AIXV3*/
  85. #ifdef SVR4
  86. #include <stropts.h>
  87. #ifdef sun
  88. #include <sys/systeminfo.h>
  89. #endif
  90. #endif
  91. #endif
  92. #if defined(__STDC__) || defined(SYSTEM_FIVE) || defined(SYSV) || defined(sun)
  93. #include <string.h>
  94. #else
  95. #include <strings.h>
  96. #endif
  97. #include <pwd.h>
  98. #include <X11/Intrinsic.h>
  99. #include <X11/StringDefs.h>
  100. #include <X11/Shell.h>
  101. #include <X11/Xaw/Dialog.h>
  102. #include <X11/Xaw/Form.h>
  103. #include <X11/Xaw/List.h>
  104. #include <X11/Xaw/Label.h>
  105. #include <X11/Xaw/SimpleMenu.h>
  106. #include <X11/Xaw/SmeBSB.h>
  107. #include <X11/Xaw/SmeLine.h>
  108. #include <X11/cursorfont.h>
  109. #include "xboard.h"
  110. #include "patchlevel.h"
  111.  
  112. #include "bitmaps/s_p.bm"
  113. #include "bitmaps/s_r.bm"
  114. #include "bitmaps/s_n.bm"
  115. #include "bitmaps/s_b.bm"
  116. #include "bitmaps/s_q.bm"
  117. #include "bitmaps/s_k.bm"
  118.  
  119. #include "bitmaps/ol_p.bm"
  120. #include "bitmaps/ol_r.bm"
  121. #include "bitmaps/ol_n.bm"
  122. #include "bitmaps/ol_b.bm"
  123. #include "bitmaps/ol_q.bm"
  124. #include "bitmaps/ol_k.bm"
  125.  
  126. #include "bitmaps/sm_s_p.bm"
  127. #include "bitmaps/sm_s_r.bm"
  128. #include "bitmaps/sm_s_n.bm"
  129. #include "bitmaps/sm_s_b.bm"
  130. #include "bitmaps/sm_s_q.bm"
  131. #include "bitmaps/sm_s_k.bm"
  132.  
  133. #include "bitmaps/sm_ol_p.bm"
  134. #include "bitmaps/sm_ol_r.bm"
  135. #include "bitmaps/sm_ol_n.bm"
  136. #include "bitmaps/sm_ol_b.bm"
  137. #include "bitmaps/sm_ol_q.bm"
  138. #include "bitmaps/sm_ol_k.bm"
  139.  
  140. #include "bitmaps/xs_s_p.bm"
  141. #include "bitmaps/xs_s_r.bm"
  142. #include "bitmaps/xs_s_n.bm"
  143. #include "bitmaps/xs_s_b.bm"
  144. #include "bitmaps/xs_s_q.bm"
  145. #include "bitmaps/xs_s_k.bm"
  146.  
  147. #include "bitmaps/xs_ol_p.bm"
  148. #include "bitmaps/xs_ol_r.bm"
  149. #include "bitmaps/xs_ol_n.bm"
  150. #include "bitmaps/xs_ol_b.bm"
  151. #include "bitmaps/xs_ol_q.bm"
  152. #include "bitmaps/xs_ol_k.bm"
  153.  
  154. #include "bitmaps/icon.bm"
  155.  
  156. int establish P((char *host, int port));
  157. void read_from_player P((caddr_t client_data, int *file_num, XtInputId *id));
  158. void read_from_ics P((caddr_t client_data, int *file_num, XtInputId *id));
  159. void main P((int argc, char **argv));
  160. void CreateGCs P((void));
  161. void CreatePieces P((void));
  162. void CreatePieceMenus P((void));
  163. char *FindFont P((char *pattern, int targetPxlSize));
  164. void PieceMenuPopup P((Widget w, XEvent *event,
  165.                String *params, Cardinal *num_params));
  166. static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
  167. static void SetWhiteToPlay P((Widget w, XEvent *event,
  168.                   String *prms, Cardinal *nprms));
  169. static void SetBlackToPlay P((Widget w, XEvent *event,
  170.                   String *prms, Cardinal *nprms));
  171. void ReadBitmap P((String name, Pixmap *pm, char large_bits[],
  172.            char medium_bits[], char small_bits[]));
  173. void CreateGrid P((void));
  174. int EventToSquare P((int x));
  175. ChessSquare CharToPiece P((int c));
  176. char PieceToChar P((ChessSquare p));
  177. ChessSquare PromoPiece P((ChessMove move_type));
  178. ChessMove CoordsToAlgebraic P((int fromX, int fromY, int toX, int toY,
  179.                    int promoChar, int currentBoardIndex,
  180.                    char out[MOVE_LEN]));
  181. void DrawSquare P((int row, int column, ChessSquare piece));
  182. void EventProc P((Widget widget, caddr_t unused, XEvent *event));
  183. void DrawPosition P((Widget w, XEvent *event,
  184.              String *prms, Cardinal *nprms));
  185. void InitPosition P((int/*Boolean*/ redraw));
  186. void CopyBoard P((Board to, Board from));
  187. Boolean CompareBoards P((Board board1, Board board2));
  188. void SendCurrentBoard P((FILE *fp));
  189. void SendBoard P((FILE *fp, Board board));
  190. void HandleUserMove P((Widget w, XEvent *event,
  191.                String *prms, Cardinal *nprms));
  192. void FinishUserMove P((ChessMove move_type, int to_x, int to_y));
  193. void HandleMachineMove P((char *message, FILE *fp));
  194. void LoadGameLoop P((void));
  195. Boolean LoadGameOneMove P((void));
  196. void ApplyMove P((ChessMove *move_type, int from_x, int from_y,
  197.           int to_x, int to_y, Board board));
  198. void MakeMove P((ChessMove *move_type, int from_x, int from_y,
  199.          int to_x, int to_y));
  200. void InitChessProgram P((char *host_name, char *program_name, int *pid,
  201.              FILE **to, FILE **from, XtIntervalId *xid,
  202.              int *sendTime));
  203. void GameEnds P((char *message));
  204. void ShutdownChessPrograms P((char *message));
  205. void CommentPopUp P((char *label));
  206. void CommentPopDown P((void));
  207. void FileNamePopUp P((char *label, char *def,
  208.               Boolean (*proc)(char *name)));
  209. void FileNameCallback P((Widget w, XtPointer client_data,
  210.              XtPointer call_data));
  211. void FileNameAction P((Widget w, XEvent *event,
  212.                String *prms, Cardinal *nprms));
  213. void PromotionPopUp P((ChessSquare piece, int to_x, int to_y));
  214. void PromotionCallback P((Widget w, XtPointer client_data,
  215.               XtPointer call_data));
  216. void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
  217. void ModeHighlight P((void));
  218. void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  219. void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  220. void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  221. void DeclineDrawProc P((Widget w, XEvent *event,
  222.             String *prms, Cardinal *nprms));
  223. void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  224. void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  225. Boolean LoadGame P((char *name));
  226. void MachineBlackProc P((Widget w, XEvent *event, String *prms,
  227.              Cardinal *nprms));
  228. void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  229. void Reset P((int/*Boolean*/ redraw));
  230. void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  231. Boolean LoadPosition P((char *name));
  232. void LoadPositionProc P((Widget w, XEvent *event,
  233.              String *prms, Cardinal *nprms));
  234. void MachineWhiteProc P((Widget w, XEvent *event,
  235.              String *prms, Cardinal *nprms));
  236. void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  237. void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  238. Boolean SaveGame P((char *name));
  239. void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  240. void SwitchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  241. void EditPositionProc P((Widget w, XEvent *event,
  242.              String *prms, Cardinal *nprms));
  243. void EditPositionDone P((void));
  244. void ForceProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  245. void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  246. void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  247. Boolean SavePosition P((char *name));
  248. void SavePositionProc P((Widget w, XEvent *event,
  249.              String *prms, Cardinal *nprms));
  250. void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
  251.             Cardinal *nprms));
  252. void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  253. void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
  254. void PrintOpponents P((FILE *fp));
  255. void PrintPosition P((FILE *fp, int move));
  256. void SendToProgram P((char *message, FILE *fp));
  257. void ReceiveFromProgram P((FILE *fp, int *source, XtInputId *id));
  258. void SendSearchDepth P((FILE *fp));
  259. void SendTimeRemaining P((FILE *fp));
  260. void DisplayMessage P((char *message));
  261. void DisplayMove P((int moveNumber));
  262. void DisplayTitle P((char *title));
  263. void Attention P((int pid));
  264. void DisplayClocks P((int clock_mode));
  265. void DisplayTimerLabel P((Widget w, char *color, long timer));
  266. char *TimeString P((long tm));
  267. void Usage P((void));
  268. char *StrStr P((char *string, char *match));
  269. int StrCaseCmp P((char *s1, char *s2));
  270. int ToLower P((int c));
  271. int ToUpper P((int c));
  272. #if defined(SYSTEM_FIVE) || defined(SYSV)
  273. char *PseudoTTY P((int *ptyv));
  274. #else
  275. void CatchPipeSignal P((int dummy));
  276. #endif
  277. extern int yylex P((void));
  278. extern int yynewfile P((void));
  279. extern ChessMove yylexstr P((int boardIndex, char *s, char **next));
  280. extern ChessMove LegalityTest P((int whiteOnMove, Board board,
  281.                  int rf, int ff, int rt, int ft,
  282.                  int promoChar));
  283. void ParseMachineMove P((char *machine_move, int move_num,
  284.              ChessMove *move_type, int *from_x, int *from_y,
  285.              int *to_x, int *to_y, char *promo_char));
  286. void ParseGameHistory P((char *game));
  287. void ParseBoard8 P((char *string));
  288.  
  289. /*
  290. * XBoard depends on Xt R4 or higher
  291. */
  292. int xtVersion = XtSpecificationRelease;
  293.  
  294. int xScreen;
  295. Display *xDisplay;
  296. Window xBoardWindow;
  297. GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
  298.   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC;
  299. Pixmap solidPawnBitmap, solidRookBitmap, solidKnightBitmap,
  300.   solidBishopBitmap, solidQueenBitmap, solidKingBitmap,
  301.   outlinePawnBitmap, outlineRookBitmap, outlineKnightBitmap,
  302.   outlineBishopBitmap, outlineQueenBitmap, outlineKingBitmap, iconPixmap;
  303. Widget shellWidget, formWidget, boardWidget, commandsWidget, messageWidget,
  304.   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[6], 
  305.   commentShell, promotionShell, whitePieceMenu, blackPieceMenu;
  306. XSegment gridSegments[(BOARD_SIZE + 1) * 2];
  307. XtIntervalId firstProgramXID = 0, secondProgramXID = 0,
  308.   readGameXID = 0, timerXID = 0;
  309. Font mainFontID, coordFontID;
  310. XFontStruct *mainFontStruct, *coordFontStruct;
  311. XtAppContext appContext;
  312. Boolean (*fileProc) P((char *name));
  313. Position commentX = -1, commentY = -1;
  314.  
  315. FILE *fromFirstProgFP, *toFirstProgFP, *fromSecondProgFP,
  316.   *toSecondProgFP, *gameFileFP, *lastMsgFP;
  317. int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0,
  318.   firstProgramPID = 0, secondProgramPID = 0, telnetPID = 0,
  319.   squareSize = LARGE_SQUARE_SIZE, fromX = -1,
  320.   fromY = -1, firstMove = True, flipView = False,
  321.   commentUp = False, filenameUp = False,
  322.   blackPlaysFirst = False, startedFromSetupPosition = False,
  323.   promotionUp = False, searchTime = 0, pmFromX = -1, pmFromY = -1,
  324.   whiteFlag = False, blackFlag = False, maybeThinking = False, 
  325.   ics_input = -1, ics_output = -1, ics_user_moved = 0, ics_gamenum = -1,
  326.   ics_getting_history = False;
  327. int firstSendTime = 2, secondSendTime = 2;  /* 0=don't, 1=do, 2=test first*/
  328. IcsMode ics_mode = IcsIdle;
  329. Pixel timerForegroundPixel, timerBackgroundPixel;
  330. MatchMode matchMode = MatchFalse;
  331. GameMode gameMode = BeginningOfGame, lastGameMode = BeginningOfGame,
  332.   pausePreviousMode = BeginningOfGame;
  333. BoardSize boardSize = Large;
  334. char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2],
  335.   ptyname[24], *chessDir, *programName, ics_black[32], ics_white[32],
  336.   endMessage[MOVE_LEN * 4];
  337.  
  338. long whiteTimeRemaining, blackTimeRemaining, timeControl;
  339. long timeRemaining[2][MAX_MOVES];
  340. extern char currentMoveString[];
  341. extern char yytext[];
  342. extern int yyboardindex;
  343.      
  344. Board boards[MAX_MOVES], initialPosition = {
  345.     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
  346.     WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
  347.     { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
  348.     WhitePawn, WhitePawn, WhitePawn, WhitePawn },
  349.     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
  350.     EmptySquare, EmptySquare, EmptySquare, EmptySquare },
  351.     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
  352.     EmptySquare, EmptySquare, EmptySquare, EmptySquare },
  353.     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
  354.     EmptySquare, EmptySquare, EmptySquare, EmptySquare },
  355.     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
  356.     EmptySquare, EmptySquare, EmptySquare, EmptySquare },
  357.     { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
  358.     BlackPawn, BlackPawn, BlackPawn, BlackPawn },
  359.     { BlackRook, BlackKnight, BlackBishop, BlackQueen,
  360.     BlackKing, BlackBishop, BlackKnight, BlackRook }
  361. };
  362.      
  363. String gnuButtonStrings[] = {
  364.     "Quit",          "Machine Black", "Load Game",     "Forward",
  365.     "Reset",         "Machine White", "Save Game",     "Backward",
  366.     "Flip View",     "Force Moves",   "Load Position", "Pause",
  367.     "Edit Position", "Two Machines",  "Save Position", "Hint"
  368.   };
  369. /* must be in same order as buttonStrings! */
  370. XtActionProc gnuButtonProcs[] = {
  371.     QuitProc,         MachineBlackProc, LoadGameProc,     ForwardProc,
  372.     ResetProc,        MachineWhiteProc, SaveGameProc,     BackwardProc,
  373.     FlipViewProc,     ForceProc,        LoadPositionProc, PauseProc,
  374.     EditPositionProc, TwoMachinesProc,  SavePositionProc, HintProc,  
  375.     NULL
  376.   };
  377.  
  378. String icsButtonStrings[] = {
  379.     "Quit",          "Call Flag",    "Load Game",     "Forward",       
  380.     "Reset",         "Draw",         "Save Game",     "Backward",
  381.     "Flip View",     "Decline Draw", "Load Position", "Pause",
  382.     "Edit Position", "Resign",       "Save Position", ""
  383.   };
  384. /* must be in same order as icsButtonStrings! */
  385. XtActionProc icsButtonProcs[] = {
  386.     QuitProc,         CallFlagProc,    LoadGameProc,     ForwardProc,
  387.     ResetProc,        DrawProc,        SaveGameProc,     BackwardProc,
  388.     FlipViewProc,     DeclineDrawProc, LoadPositionProc, PauseProc,
  389.     EditPositionProc, ResignProc,      SavePositionProc, NothingProc,
  390.     NULL
  391.   };
  392.  
  393. String *buttonStrings;
  394. XtActionProc *buttonProcs;
  395. int buttonCount;
  396.  
  397. #define PIECE_MENU_SIZE 10
  398. String pieceMenuStrings[PIECE_MENU_SIZE] = {
  399.     "----", "Pawn", "Knight", "Bishop", "Rook", "Queen", "King",
  400.     "----", "Empty square", "Clear board"
  401.   };
  402. /* must be in same order as PieceMenuStrings! */
  403. ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
  404.     { (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
  405.     WhiteRook, WhiteQueen, WhiteKing,
  406.     (ChessSquare) 0, EmptySquare, ClearBoard },
  407.     { (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
  408.     BlackRook, BlackQueen, BlackKing,
  409.     (ChessSquare) 0, EmptySquare, ClearBoard },
  410. };
  411.  
  412. Arg shellArgs[] = {
  413.     { XtNwidth, 0 },
  414.     { XtNheight, 0 },
  415.     { XtNminWidth, 0 },
  416.     { XtNminHeight, 0 },
  417.     { XtNmaxWidth, 0 },
  418.     { XtNmaxHeight, 0 }
  419. };
  420.  
  421. Arg boardArgs[] = {
  422.     { XtNborderWidth, 0 },
  423.     { XtNwidth, LINE_GAP + BOARD_SIZE * (LARGE_SQUARE_SIZE + LINE_GAP) },
  424.     { XtNheight, LINE_GAP + BOARD_SIZE * (LARGE_SQUARE_SIZE + LINE_GAP) }
  425. };
  426.  
  427. Arg messageArgs[] = {
  428.     { XtNborderWidth, 0 },
  429.     { XtNjustify, (XtArgVal) XtJustifyLeft },
  430.     { XtNlabel, (XtArgVal) "starting..." }
  431. };
  432.  
  433. Arg timerArgs[] = {
  434.     { XtNborderWidth, 0 },
  435.     { XtNjustify, (XtArgVal) XtJustifyLeft }
  436. };
  437.  
  438. Arg titleArgs[] = {
  439.     { XtNborderWidth, 0 },
  440.     { XtNjustify, (XtArgVal) XtJustifyLeft }
  441. };
  442.  
  443. typedef struct {
  444.     Pixel whitePieceColor;
  445.     Pixel blackPieceColor;
  446.     Pixel lightSquareColor;
  447.     Pixel darkSquareColor;
  448.     int movesPerSession;
  449.     String initString;
  450.     String whiteString;
  451.     String blackString;
  452.     String firstChessProgram;
  453.     String secondChessProgram;
  454.     Boolean noChessProgram;
  455.     String firstHost;
  456.     String secondHost;
  457.     String solidPawnBitmap;
  458.     String solidRookBitmap;
  459.     String solidBishopBitmap;
  460.     String solidKnightBitmap;
  461.     String solidQueenBitmap;
  462.     String solidKingBitmap;
  463.     String outlinePawnBitmap;
  464.     String outlineRookBitmap;
  465.     String outlineBishopBitmap;
  466.     String outlineKnightBitmap;
  467.     String outlineQueenBitmap;
  468.     String outlineKingBitmap;
  469.     String remoteShell;
  470.     float timeDelay;
  471.     String timeControl;
  472.     Boolean icsActive;
  473.     String icsHost;
  474.     int icsPort;
  475.     Boolean useTelnet;
  476.     String telnetProgram;
  477.     String gateway;
  478.     String loadGameFile;
  479.     String saveGameFile;
  480.     Boolean autoSaveGames;
  481.     String loadPositionFile;
  482.     String savePositionFile;
  483.     String matchMode;
  484.     Boolean monoMode;
  485.     Boolean debugMode;
  486.     Boolean clockMode;
  487.     String boardSize;
  488.     Boolean Iconic;
  489.     String searchTime;
  490.     int searchDepth;
  491.     Boolean showCoords;
  492.     String mainFont;
  493.     String coordFont;
  494.     Boolean ringBellAfterMoves;
  495.     Boolean autoCallFlag;
  496.     int borderXoffset;
  497.     int borderYoffset;
  498. } AppData, *AppDataPtr;
  499.  
  500. AppData appData;
  501.  
  502. XtResource clientResources[] = {
  503.     { "whitePieceColor", "WhitePieceColor", XtRPixel, sizeof(Pixel),
  504.     XtOffset(AppDataPtr, whitePieceColor), XtRString,
  505.     WHITE_PIECE_COLOR },
  506.     { "blackPieceColor", "BlackPieceColor", XtRPixel, sizeof(Pixel),
  507.     XtOffset(AppDataPtr, blackPieceColor), XtRString,
  508.     BLACK_PIECE_COLOR },
  509.     { "lightSquareColor", "LightSquareColor", XtRPixel,
  510.     sizeof(Pixel), XtOffset(AppDataPtr, lightSquareColor),
  511.     XtRString, LIGHT_SQUARE_COLOR }, 
  512.     { "darkSquareColor", "DarkSquareColor", XtRPixel, sizeof(Pixel),
  513.     XtOffset(AppDataPtr, darkSquareColor), XtRString,
  514.     DARK_SQUARE_COLOR },
  515.     { "movesPerSession", "movesPerSession", XtRInt, sizeof(int),
  516.     XtOffset(AppDataPtr, movesPerSession), XtRImmediate,
  517.     (XtPointer) MOVES_PER_SESSION },
  518.     { "initString", "initString", XtRString, sizeof(String),
  519.     XtOffset(AppDataPtr, initString), XtRString, INIT_STRING },
  520.     { "whiteString", "whiteString", XtRString, sizeof(String),
  521.     XtOffset(AppDataPtr, whiteString), XtRString, WHITE_STRING },
  522.     { "blackString", "blackString", XtRString, sizeof(String),
  523.     XtOffset(AppDataPtr, blackString), XtRString, BLACK_STRING },
  524.     { "firstChessProgram", "firstChessProgram", XtRString,
  525.     sizeof(String), XtOffset(AppDataPtr, firstChessProgram),
  526.     XtRString, FIRST_CHESS_PROGRAM },
  527.     { "secondChessProgram", "secondChessProgram", XtRString,
  528.     sizeof(String), XtOffset(AppDataPtr, secondChessProgram),
  529.     XtRString, SECOND_CHESS_PROGRAM },
  530.     { "noChessProgram", "noChessProgram", XtRBoolean,
  531.     sizeof(Boolean), XtOffset(AppDataPtr, noChessProgram),
  532.     XtRImmediate, (XtPointer) False },
  533.     { "firstHost", "firstHost", XtRString, sizeof(String),
  534.     XtOffset(AppDataPtr, firstHost), XtRString, FIRST_HOST },
  535.     { "secondHost", "secondHost", XtRString, sizeof(String),
  536.     XtOffset(AppDataPtr, secondHost), XtRString, SECOND_HOST },
  537.     { "solidPawnBitmap", "solidPawnBitmap", XtRString,
  538.     sizeof(String), XtOffset(AppDataPtr, solidPawnBitmap),
  539.     XtRString, "" },
  540.     { "solidRookBitmap", "solidRookBitmap", XtRString,
  541.     sizeof(String), XtOffset(AppDataPtr, solidRookBitmap),
  542.     XtRString, "" },
  543.     { "solidKnightBitmap", "solidKnightBitmap", XtRString,
  544.     sizeof(String), XtOffset(AppDataPtr, solidKnightBitmap),
  545.     XtRString, "" },
  546.     { "solidBishopBitmap", "solidBishopBitmap", XtRString,
  547.     sizeof(String), XtOffset(AppDataPtr, solidBishopBitmap),
  548.     XtRString, "" },
  549.     { "solidQueenBitmap", "solidQueenBitmap", XtRString,
  550.     sizeof(String), XtOffset(AppDataPtr, solidQueenBitmap),
  551.     XtRString, "" },
  552.     { "solidKingBitmap", "solidKingBitmap", XtRString,
  553.     sizeof(String), XtOffset(AppDataPtr, solidKingBitmap),
  554.     XtRString, "" },
  555.     { "outlinePawnBitmap", "outlinePawnBitmap", XtRString,
  556.     sizeof(String), XtOffset(AppDataPtr, outlinePawnBitmap),
  557.     XtRString, "" },
  558.     { "outlineRookBitmap", "outlineRookBitmap", XtRString,
  559.     sizeof(String), XtOffset(AppDataPtr, outlineRookBitmap),
  560.     XtRString, "" },
  561.     { "outlineKnightBitmap", "outlineKnightBitmap", XtRString,
  562.     sizeof(String), XtOffset(AppDataPtr, outlineKnightBitmap),
  563.     XtRString, "" },
  564.     { "outlineBishopBitmap", "outlineBishopBitmap", XtRString,
  565.     sizeof(String), XtOffset(AppDataPtr, outlineBishopBitmap),
  566.     XtRString, "" },
  567.     { "outlineQueenBitmap", "outlineQueenBitmap", XtRString,
  568.     sizeof(String), XtOffset(AppDataPtr, outlineQueenBitmap),
  569.     XtRString, "" },
  570.     { "outlineKingBitmap", "outlineKingBitmap", XtRString,
  571.     sizeof(String), XtOffset(AppDataPtr, outlineKingBitmap),
  572.     XtRString, "" },
  573.     { "remoteShell", "remoteShell", XtRString, sizeof(String),
  574.     XtOffset(AppDataPtr, remoteShell), XtRString, "rsh" },
  575.     { "timeDelay", "timeDelay", XtRFloat, sizeof(float),
  576.     XtOffset(AppDataPtr, timeDelay), XtRString,
  577.     (XtPointer) TIME_DELAY },
  578.     { "timeControl", "timeControl", XtRString, sizeof(String),
  579.     XtOffset(AppDataPtr, timeControl), XtRString,
  580.     (XtPointer) TIME_CONTROL },
  581.     { "internetChessServerMode", "internetChessServerMode",
  582.     XtRBoolean, sizeof(Boolean),
  583.     XtOffset(AppDataPtr, icsActive), XtRImmediate,
  584.     (XtPointer) False },
  585.     { "internetChessServerHost", "internetChessServerHost",
  586.     XtRString, sizeof(String),
  587.     XtOffset(AppDataPtr, icsHost),
  588.     XtRString, (XtPointer) ICS_HOST },
  589.     { "internetChessServerPort", "internetChessServerPort",
  590.     XtRInt, sizeof(int),
  591.     XtOffset(AppDataPtr, icsPort), XtRImmediate,
  592.     (XtPointer) ICS_PORT },
  593.     { "useTelnet", "useTelnet", XtRBoolean, sizeof(Boolean),
  594.     XtOffset(AppDataPtr, useTelnet), XtRImmediate,
  595.     (XtPointer) False },
  596.     { "telnetProgram", "telnetProgram", XtRString, sizeof(String),
  597.     XtOffset(AppDataPtr, telnetProgram), XtRString, "telnet" },
  598.     { "gateway", "gateway", XtRString, sizeof(String),
  599.     XtOffset(AppDataPtr, gateway), XtRString, "" },
  600.     { "loadGameFile", "loadGameFile", XtRString, sizeof(String),
  601.     XtOffset(AppDataPtr, loadGameFile), XtRString, "" },
  602.     { "saveGameFile", "saveGameFile", XtRString, sizeof(String),
  603.     XtOffset(AppDataPtr, saveGameFile), XtRString, "" },
  604.     { "autoSaveGames", "autoSaveGames", XtRBoolean,
  605.     sizeof(Boolean), XtOffset(AppDataPtr, autoSaveGames),
  606.     XtRImmediate, (XtPointer) False },
  607.     { "loadPositionFile", "loadPositionFile", XtRString,
  608.     sizeof(String), XtOffset(AppDataPtr, loadPositionFile),
  609.     XtRString, "" },
  610.     { "savePositionFile", "savePositionFile", XtRString,
  611.     sizeof(String), XtOffset(AppDataPtr, savePositionFile),
  612.     XtRString, "" },
  613.     { "matchMode", "matchMode", XtRString, sizeof(String),
  614.     XtOffset(AppDataPtr, matchMode), XtRString, MATCH_MODE },
  615.     { "monoMode", "monoMode", XtRBoolean, sizeof(Boolean),
  616.     XtOffset(AppDataPtr, monoMode), XtRImmediate,
  617.     (XtPointer) False },
  618.     { "debugMode", "debugMode", XtRBoolean, sizeof(Boolean),
  619.     XtOffset(AppDataPtr, debugMode), XtRImmediate,
  620.     (XtPointer) False },
  621.     { "Iconic", "Iconic", XtRBoolean, sizeof(Boolean),
  622.     XtOffset(AppDataPtr, Iconic), XtRImmediate,
  623.     (XtPointer) False },
  624.     { "clockMode", "clockMode", XtRBoolean, sizeof(Boolean),
  625.     XtOffset(AppDataPtr, clockMode), XtRImmediate,
  626.     (XtPointer) True },
  627.     { "autoCallFlag", "autoCallFlag", XtRBoolean,
  628.     sizeof(Boolean), XtOffset(AppDataPtr, autoCallFlag),
  629.     XtRImmediate, (XtPointer) False },
  630.     { "boardSize", "boardSize", XtRString, sizeof(String),
  631.     XtOffset(AppDataPtr, boardSize), XtRString, DEFAULT_SIZE },
  632.     { "searchTime", "searchTime", XtRString, sizeof(String),
  633.     XtOffset(AppDataPtr, searchTime), XtRString,
  634.     (XtPointer) "" },
  635.     { "searchDepth", "searchDepth", XtRInt, sizeof(int),
  636.     XtOffset(AppDataPtr, searchDepth), XtRImmediate, 
  637.     (XtPointer) 0 },
  638.     { "showCoords", "showCoords", XtRBoolean, sizeof(Boolean),
  639.     XtOffset(AppDataPtr, showCoords), XtRImmediate,
  640.     (XtPointer) False },
  641.     { "mainFont", "mainFont", XtRString, sizeof(String),
  642.     XtOffset(AppDataPtr, mainFont), XtRString, MAIN_FONT },
  643.     { "coordFont", "coordFont", XtRString, sizeof(String),
  644.     XtOffset(AppDataPtr, coordFont), XtRString, COORD_FONT },
  645.     { "ringBellAfterMoves", "ringBellAfterMoves",
  646.     XtRBoolean, sizeof(Boolean),
  647.     XtOffset(AppDataPtr, ringBellAfterMoves),
  648.     XtRImmediate, (XtPointer) False    },
  649.     { "borderXoffset", "borderXoffset", XtRInt, sizeof(int),
  650.     XtOffset(AppDataPtr, borderXoffset), XtRImmediate,
  651.     (XtPointer) BORDER_X_OFFSET },
  652.     { "borderYoffset", "borderYOffset", XtRInt, sizeof(int),
  653.     XtOffset(AppDataPtr, borderYoffset), XtRImmediate,
  654.     (XtPointer) BORDER_Y_OFFSET },
  655. };
  656.  
  657. Pixmap *pieceToSolid[] = {
  658.     &solidPawnBitmap, &solidRookBitmap, &solidKnightBitmap,
  659.     &solidBishopBitmap, &solidQueenBitmap, &solidKingBitmap,
  660.     &solidPawnBitmap, &solidRookBitmap, &solidKnightBitmap,
  661.     &solidBishopBitmap, &solidQueenBitmap, &solidKingBitmap
  662.   };
  663.  
  664. Pixmap *pieceToOutline[] = {
  665.     &outlinePawnBitmap, &outlineRookBitmap, &outlineKnightBitmap,
  666.     &outlineBishopBitmap, &outlineQueenBitmap, &outlineKingBitmap,
  667.     &outlinePawnBitmap, &outlineRookBitmap, &outlineKnightBitmap,
  668.     &outlineBishopBitmap, &outlineQueenBitmap, &outlineKingBitmap
  669.   };
  670.  
  671. char pieceToChar[] = {
  672.     'P', 'R', 'N', 'B', 'Q', 'K',
  673.     'p', 'r', 'n', 'b', 'q', 'k', '.'
  674.   };
  675.  
  676. XrmOptionDescRec shellOptions[] = {
  677.     { "-movesPerSession", "movesPerSession", XrmoptionSepArg, NULL },
  678.     { "-mps", "movesPerSession", XrmoptionSepArg, NULL },
  679.     { "-internetChessServerMode", "internetChessServerMode",
  680.     XrmoptionSepArg, NULL },
  681.     { "-ics", "internetChessServerMode", XrmoptionSepArg, NULL },
  682.     { "-internetChessServerPort", "internetChessServerPort",
  683.     XrmoptionSepArg, NULL },
  684.     { "-icsport", "internetChessServerPort", XrmoptionSepArg, NULL },
  685.     { "-internetChessServerHost", "internetChessServerHost",
  686.     XrmoptionSepArg, NULL },
  687.     { "-icshost", "internetChessServerHost", XrmoptionSepArg, NULL },
  688.     { "-firstChessProgram", "firstChessProgram", XrmoptionSepArg, NULL },
  689.     { "-fcp", "firstChessProgram", XrmoptionSepArg, NULL },
  690.     { "-secondChessProgram", "secondChessProgram", XrmoptionSepArg, NULL },
  691.     { "-scp", "secondChessProgram", XrmoptionSepArg, NULL },
  692.     { "-noChessProgram", "noChessProgram", XrmoptionSepArg, NULL },
  693.     { "-ncp", "noChessProgram", XrmoptionSepArg, NULL },
  694.     { "-firstHost", "firstHost", XrmoptionSepArg, NULL },
  695.     { "-fh", "firstHost", XrmoptionSepArg, NULL },
  696.     { "-secondHost", "secondHost", XrmoptionSepArg, NULL },
  697.     { "-sh", "secondHost", XrmoptionSepArg, NULL },
  698.     { "-remoteShell", "remoteShell", XrmoptionSepArg, NULL },
  699.     { "-rsh", "remoteShell", XrmoptionSepArg, NULL },
  700.     { "-timeDelay", "timeDelay", XrmoptionSepArg, NULL },
  701.     { "-td", "timeDelay", XrmoptionSepArg, NULL },
  702.     { "-timeControl", "timeControl", XrmoptionSepArg, NULL },
  703.     { "-tc", "timeControl", XrmoptionSepArg, NULL },
  704.     { "-loadGameFile", "loadGameFile", XrmoptionSepArg, NULL },
  705.     { "-lgf", "loadGameFile", XrmoptionSepArg, NULL },
  706.     { "-saveGameFile", "saveGameFile", XrmoptionSepArg, NULL },
  707.     { "-sgf", "saveGameFile", XrmoptionSepArg, NULL },
  708.     { "-autoSaveGames", "autoSaveGames", XrmoptionSepArg, NULL },
  709.     { "-autosave", "autoSaveGames", XrmoptionSepArg, NULL },
  710.     { "-loadPositionFile", "loadPositionFile", XrmoptionSepArg, NULL },
  711.     { "-lpf", "loadPositionFile", XrmoptionSepArg, NULL },
  712.     { "-savePositionFile", "savePositionFile", XrmoptionSepArg, NULL },
  713.     { "-spf", "savePositionFile", XrmoptionSepArg, NULL },
  714.     { "-matchMode", "matchMode", XrmoptionSepArg, NULL },
  715.     { "-mm", "matchMode", XrmoptionSepArg, NULL },
  716.     { "-monoMode", "monoMode", XrmoptionSepArg, NULL },
  717.     { "-mono", "monoMode", XrmoptionSepArg, NULL },
  718.     { "-debugMode", "debugMode", XrmoptionSepArg, NULL },
  719.     { "-debug", "debugMode", XrmoptionSepArg, NULL },
  720.     { "-clockMode", "clockMode", XrmoptionSepArg, NULL },
  721.     { "-clock", "clockMode", XrmoptionSepArg, NULL },
  722.     { "-autoCallFlag", "autoCallFlags", XrmoptionSepArg, NULL },
  723.     { "-autoflag", "autoCallFlag", XrmoptionSepArg, NULL },
  724.     { "-boardSize", "boardSize", XrmoptionSepArg, NULL },
  725.     { "-size", "boardSize", XrmoptionSepArg, NULL },
  726.     { "-searchTime", "searchTime", XrmoptionSepArg, NULL },
  727.     { "-st", "searchTime", XrmoptionSepArg, NULL },
  728.     { "-searchDepth", "searchDepth", XrmoptionSepArg, NULL },
  729.     { "-sd", "searchDepth", XrmoptionSepArg, NULL },
  730.     { "-showCoords", "showCoords", XrmoptionSepArg, NULL },
  731.     { "-coords", "showCoords", XrmoptionSepArg, NULL },
  732.     { "-bell", "ringBellAfterMoves", XrmoptionSepArg, NULL },
  733.     { "-ringBellAfterMoves", "ringBellAfterMoves", XrmoptionSepArg, NULL },
  734.     { "-iconic", "Iconic", XrmoptionNoArg, "True" }
  735. };
  736.  
  737. XtActionsRec boardActions[] = {
  738.     { "DrawPosition", DrawPosition },
  739.     { "HandleUserMove", HandleUserMove },
  740.     { "ResetProc", ResetProc },
  741.     { "LoadGameProc", LoadGameProc },
  742.     { "QuitProc", QuitProc },
  743.     { "ForwardProc", ForwardProc },
  744.     { "BackwardProc", BackwardProc },
  745.     { "PauseProc", PauseProc },
  746.     { "Iconify", Iconify },
  747.     { "FileNameAction", FileNameAction },
  748.     { "PieceMenuPopup", PieceMenuPopup },
  749.     { "SetWhiteToPlay", SetWhiteToPlay },
  750.     { "SetBlackToPlay", SetBlackToPlay }
  751. };
  752.      
  753. char translationsTable[] =
  754.   "<Expose>: DrawPosition() \n \
  755.    <Btn1Down>: HandleUserMove() \n \
  756.    <Btn1Up>: HandleUserMove() \n \
  757.    <Btn2Down>: XawPositionSimpleMenu(menuW) PieceMenuPopup(menuW) \n \
  758.    <Btn3Down>: XawPositionSimpleMenu(menuB) PieceMenuPopup(menuB) \n \
  759.    <Key>r: ResetProc() \n \
  760.    <Key>R: ResetProc() \n \
  761.    <Key>g: LoadGameProc() \n \
  762.    <Key>G: LoadGameProc() \n \
  763.    <Key>q: QuitProc() \n \
  764.    <Key>Q: QuitProc() \n \
  765.    <Message>WM_PROTOCOLS: QuitProc() \n \
  766.    <Key>f: ForwardProc() \n \
  767.    <Key>F: ForwardProc() \n \
  768.    <Key>b: BackwardProc() \n \
  769.    <Key>B: BackwardProc() \n \
  770.    <Key>p: PauseProc() \n \
  771.    <Key>P: PauseProc() \n \
  772.    <Key>i: Iconify() \n \
  773.    <Key>I: Iconify() \n \
  774.    <Key>c: Iconify() \n \
  775.    <Key>C: Iconify() \n";
  776.      
  777. char whiteTranslations[] = "<BtnDown>: SetWhiteToPlay()\n";
  778. char blackTranslations[] = "<BtnDown>: SetBlackToPlay()\n";
  779.      
  780. String xboardResources[] = {
  781.     DEFAULT_FONT,
  782.     "*Dialog*value.translations: #override \\n <Key>Return: FileNameAction()",
  783.     NULL
  784.   };
  785.      
  786. void main(argc, argv)
  787.      int argc;
  788.      char **argv;
  789. {
  790.     int ok, i, mainFontPxlSize, coordFontPxlSize;
  791.     int min, sec, matched;
  792.     XSetWindowAttributes window_attributes;
  793.     char buf[MSG_SIZ];
  794.     Arg args[10];
  795.     Dimension timerWidth, boardWidth, commandsWidth, w, h;
  796.     XFontStruct *labelFontStruct;
  797.     
  798.     setbuf(stdout, NULL);
  799.     setbuf(stderr, NULL);
  800.     
  801.     programName = strrchr(argv[0], '/');
  802.     if (programName == NULL)
  803.       programName = argv[0];
  804.     else
  805.       programName++;
  806.     
  807.     shellWidget =
  808.       XtAppInitialize(&appContext, "XBoard", shellOptions,
  809.               XtNumber(shellOptions), &argc, argv,
  810.               xboardResources, NULL, 0);
  811.     if (argc > 1)
  812.       Usage();
  813.     
  814.     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
  815.     chessDir = ".";
  816.     } else {
  817.     if (chdir(chessDir) != 0) {
  818.         fprintf(stderr, "%s: can't cd to CHESSDIR: ", programName);
  819.         perror(chessDir);
  820.         exit(1);
  821.     }
  822.     }
  823.     
  824.     XtGetApplicationResources(shellWidget, &appData, clientResources,
  825.                   XtNumber(clientResources), NULL, 0);
  826.     
  827.     /*
  828.      * Determine matchMode state -- poor man's resource converter
  829.      */
  830.     if (StrCaseCmp(appData.matchMode, "Init") == 0)
  831.       matchMode = MatchInit;
  832.     else if (StrCaseCmp(appData.matchMode, "Position") == 0)
  833.       matchMode = MatchPosition;
  834.     else if (StrCaseCmp(appData.matchMode, "Opening") == 0)
  835.       matchMode = MatchOpening;
  836.     else if (StrCaseCmp(appData.matchMode, "False") == 0)
  837.       matchMode = MatchFalse;
  838.     else {
  839.     fprintf(stderr, "%s: bad matchMode option %s\n",
  840.         programName, appData.matchMode);
  841.     Usage();
  842.     }
  843.     
  844.     /*
  845.      * Parse internet chess server status
  846.      */
  847.     if (appData.icsActive) {
  848.     ok = establish(appData.icsHost,
  849.                (unsigned short)appData.icsPort);
  850.     if (ok == -1) {
  851.         fprintf(stderr,
  852.             "%s: could not connect to host %s, port %d: ",
  853.             programName, appData.icsHost, appData.icsPort);
  854.         perror("");
  855.         exit(1);
  856.     }
  857.     appData.noChessProgram = True;
  858.     buttonStrings = icsButtonStrings;
  859.     buttonProcs = icsButtonProcs;
  860.     buttonCount = XtNumber(icsButtonStrings);
  861.     } else {
  862.     buttonStrings = gnuButtonStrings;
  863.     buttonProcs = gnuButtonProcs;
  864.     buttonCount = XtNumber(gnuButtonStrings);
  865.     }
  866.     
  867.     /*
  868.      * Parse timeControl resource
  869.      */
  870.     matched = sscanf(appData.timeControl, "%d:%d", &min, &sec);
  871.     if (matched == 1) {
  872.     timeControl = min * 60 * 1000;
  873.     } else if (matched == 2) {
  874.     timeControl = (min * 60 + sec) * 1000;
  875.     } else {
  876.     fprintf(stderr, "%s: bad timeControl option %s\n",
  877.         programName, appData.timeControl);
  878.     Usage();
  879.     }
  880.     if (appData.icsActive) timeControl = 0;
  881.     
  882.     /*
  883.      * Parse searchTime resource
  884.      */
  885.     if (*appData.searchTime != NULLCHAR) {
  886.     matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
  887.     if (matched == 1) {
  888.         searchTime = min * 60;
  889.     } else if (matched == 2) {
  890.         searchTime = min * 60 + sec;
  891.     } else {
  892.         fprintf(stderr, "%s: bad searchTime option %s\n",
  893.             programName, appData.searchTime);
  894.         Usage();
  895.     }
  896.     }
  897.     
  898.     /*
  899.      * Determine boardSize
  900.      */
  901.     if (StrCaseCmp(appData.boardSize, "Large") == 0)
  902.       boardSize = Large;
  903.     else if (StrCaseCmp(appData.boardSize, "Medium") == 0)
  904.       boardSize = Medium;
  905.     else if (StrCaseCmp(appData.boardSize, "Small") == 0)
  906.       boardSize = Small;
  907.     else {
  908.     fprintf(stderr, "%s: bad boardSize option %s\n",
  909.         programName, appData.boardSize);
  910.     Usage();
  911.     }
  912.     xDisplay = XtDisplay(shellWidget);
  913.     xScreen = DefaultScreen(xDisplay);
  914.     if (((DisplayWidth(xDisplay, xScreen) < 800) ||
  915.      (DisplayHeight(xDisplay, xScreen) < 800))
  916.     && (boardSize == Large)) {
  917.     boardSize = Medium;
  918.     }
  919.     switch (boardSize) {
  920.       case Small:
  921.     squareSize = SMALL_SQUARE_SIZE;
  922.     mainFontPxlSize = 11;
  923.     coordFontPxlSize = 10;
  924.     break;
  925.       case Medium:
  926.     squareSize = MEDIUM_SQUARE_SIZE;
  927.     mainFontPxlSize = 17;
  928.     coordFontPxlSize = 12;
  929.     break;
  930.       case Large:
  931.     squareSize = LARGE_SQUARE_SIZE;
  932.     mainFontPxlSize = 17;
  933.     coordFontPxlSize = 14;
  934.     break;
  935.     }
  936.     boardWidth = LINE_GAP + BOARD_SIZE * (squareSize + LINE_GAP);
  937.     XtSetArg(boardArgs[1], XtNwidth, boardWidth);
  938.     XtSetArg(boardArgs[2], XtNheight,
  939.          LINE_GAP + BOARD_SIZE * (squareSize + LINE_GAP));
  940.     
  941.     /*
  942.      * Determine what fonts to use.
  943.      */
  944.     appData.mainFont = FindFont(appData.mainFont, mainFontPxlSize);
  945.     mainFontID = XLoadFont(xDisplay, appData.mainFont);
  946.     mainFontStruct = XQueryFont(xDisplay, mainFontID);
  947.     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
  948.     coordFontID = XLoadFont(xDisplay, appData.coordFont);
  949.     coordFontStruct = XQueryFont(xDisplay, coordFontID);
  950.  
  951.     if ((*appData.searchTime != NULLCHAR) || (appData.searchDepth > 0)
  952.     || appData.noChessProgram)
  953.       appData.clockMode = False;
  954.     if (appData.icsActive) appData.clockMode = True;
  955.     
  956.     /*
  957.      * Detect if there are not enough colors are available and adapt.
  958.      */
  959.     if (DefaultDepth(xDisplay, xScreen) <= 2)
  960.       appData.monoMode = True;
  961.     
  962.     /*
  963.      * widget hierarchy
  964.      */
  965.     formWidget =
  966.       XtCreateManagedWidget("form", formWidgetClass, shellWidget, NULL, 0);
  967.     
  968.     widgetList[0] = whiteTimerWidget =
  969.       XtCreateWidget("white time:", labelWidgetClass,
  970.              formWidget, timerArgs, XtNumber(timerArgs));
  971.     XtSetArg(args[0], XtNfont, mainFontStruct);
  972.     XtSetValues(whiteTimerWidget, args, 1);
  973.     
  974.     widgetList[1] = blackTimerWidget =
  975.       XtCreateWidget("black time:", labelWidgetClass,
  976.              formWidget, timerArgs, XtNumber(timerArgs));
  977.     XtSetArg(args[0], XtNfont, mainFontStruct);
  978.     XtSetValues(blackTimerWidget, args, 1);
  979.     
  980.     widgetList[2] = titleWidget =
  981.       XtCreateWidget("", labelWidgetClass,
  982.              formWidget, titleArgs, XtNumber(titleArgs));
  983.     XtSetArg(args[0], XtNfont, mainFontStruct);
  984.     XtSetValues(titleWidget, args, 1);
  985.     
  986.     widgetList[3] = messageWidget =
  987.       XtCreateWidget("message", labelWidgetClass, formWidget,
  988.              messageArgs, XtNumber(messageArgs));
  989.     XtSetArg(args[0], XtNfont, mainFontStruct);
  990.     XtSetValues(messageWidget, args, 1);
  991.     
  992.     i = 0;
  993.     XtSetArg(args[i], XtNborderWidth, 0);                  i++;
  994.     XtSetArg(args[i], XtNdefaultColumns, 4);               i++;
  995.     XtSetArg(args[i], XtNforceColumns, True);              i++;
  996.     XtSetArg(args[i], XtNcolumnSpacing, 12);               i++;
  997.     XtSetArg(args[i], XtNlist, (XtArgVal) buttonStrings);  i++;
  998.     XtSetArg(args[i], XtNnumberStrings, buttonCount);      i++;
  999.     XtSetArg(args[i], XtNfont, mainFontStruct);            i++;
  1000.     widgetList[4] = commandsWidget =
  1001.       XtCreateWidget("commands", listWidgetClass, formWidget, args, i);
  1002.     
  1003.     widgetList[5] = boardWidget =
  1004.       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
  1005.              XtNumber(boardArgs));
  1006.     
  1007.     XtManageChildren(widgetList, XtNumber(widgetList));
  1008.     
  1009.     /*
  1010.      * Calculate the width of the timer labels.
  1011.      */
  1012.     XtSetArg(args[0], XtNfont, &labelFontStruct);
  1013.     XtGetValues(whiteTimerWidget, args, 1);
  1014.     if (appData.clockMode) {
  1015.     if (timeControl == 0) 
  1016.       sprintf(buf, "White: %s ", TimeString(2 * 60 * 60 * 1000));
  1017.     else
  1018.       sprintf(buf, "White: %s ", TimeString(-timeControl));
  1019.     timerWidth = XTextWidth(labelFontStruct, buf, strlen(buf) - 1);
  1020.     } else {
  1021.     timerWidth = XTextWidth(labelFontStruct, "White  ", 7);
  1022.     }
  1023.     XtSetArg(args[0], XtNwidth, timerWidth);
  1024.     XtSetValues(whiteTimerWidget, args, 1);
  1025.     XtSetValues(blackTimerWidget, args, 1);
  1026.     
  1027.     XtSetArg(args[0], XtNbackground, &timerForegroundPixel);
  1028.     XtSetArg(args[1], XtNforeground, &timerBackgroundPixel);
  1029.     XtGetValues(whiteTimerWidget, args, 2);
  1030.     
  1031.     /*
  1032.      * Calculate the width of the title and message labels.
  1033.      */
  1034.     XtSetArg(args[0], XtNwidth, &commandsWidth);
  1035.     XtGetValues(commandsWidget, args, 1);
  1036.     w = (commandsWidth > boardWidth) ? commandsWidth : boardWidth;
  1037.     XtSetArg(args[0], XtNwidth, w - timerWidth*2 - 12);
  1038.     XtSetValues(titleWidget, args, 1);
  1039.     XtSetArg(args[0], XtNwidth, w - 8);
  1040.     XtSetValues(messageWidget, args, 1);
  1041.     
  1042.     /*
  1043.      * formWidget uses these constraints but they are stored
  1044.      * in the children.
  1045.      */
  1046.     XtSetArg(args[0], XtNfromHoriz, whiteTimerWidget);
  1047.     XtSetValues(blackTimerWidget, args, 1);
  1048.     XtSetArg(args[0], XtNfromHoriz, blackTimerWidget);
  1049.     XtSetValues(titleWidget, args, 1);
  1050.     XtSetArg(args[0], XtNfromVert, whiteTimerWidget);
  1051.     XtSetValues(messageWidget, args, 1);
  1052.     XtSetArg(args[0], XtNfromVert, messageWidget);
  1053.     XtSetValues(commandsWidget, args, 1);
  1054.     XtSetArg(args[0], XtNfromVert, commandsWidget);
  1055.     XtSetValues(boardWidget, args, 1);
  1056.     
  1057.     if (appData.icsActive) {
  1058.     XtAppAddInput(appContext, ics_input,
  1059.               (XtPointer) (XtInputExceptMask|XtInputReadMask),
  1060.               (XtInputCallbackProc) read_from_ics,
  1061.               (XtPointer) NULL);
  1062.     XtAppAddInput(appContext, fileno(stdin),
  1063.               (XtPointer) XtInputReadMask,
  1064.               (XtInputCallbackProc) read_from_player,
  1065.               (XtPointer) NULL);
  1066.     } 
  1067.     
  1068.     XtRealizeWidget(shellWidget);
  1069.     
  1070.     xBoardWindow = XtWindow(boardWidget);
  1071.     
  1072.     /*
  1073.      * Create an icon.
  1074.      */
  1075.     iconPixmap = XCreateBitmapFromData(xDisplay, XtWindow(shellWidget),
  1076.                        icon_bits, icon_width, icon_height);
  1077.     XtSetArg(args[0], XtNiconPixmap, iconPixmap);
  1078.     XtSetValues(shellWidget, args, 1);
  1079.     
  1080.     /*
  1081.      * Create a cursor for the board widget.
  1082.      */
  1083.     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
  1084.     XChangeWindowAttributes(xDisplay, xBoardWindow,
  1085.                 CWCursor, &window_attributes);
  1086.     
  1087.     /*
  1088.      * Inhibit shell resizing.
  1089.      */
  1090.     shellArgs[0].value = (XtArgVal) &w;
  1091.     shellArgs[1].value = (XtArgVal) &h;
  1092.     XtGetValues(shellWidget, shellArgs, 2);
  1093.     shellArgs[4].value = shellArgs[2].value = w;
  1094.     shellArgs[5].value = shellArgs[3].value = h;
  1095.     XtSetValues(shellWidget, &shellArgs[2], 4);
  1096.     
  1097.     CreateGCs();
  1098.     CreateGrid();
  1099.     CreatePieces();
  1100.     CreatePieceMenus();
  1101.     
  1102.     XtAddCallback(commandsWidget, XtNcallback, SelectCommand, NULL);
  1103.     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
  1104.     
  1105.     XtSetArg(args[0], XtNtranslations,
  1106.          XtParseTranslationTable(translationsTable));
  1107.     XtSetValues(boardWidget, &args[0], 1);
  1108.     XtSetArg(args[0], XtNtranslations,
  1109.          XtParseTranslationTable(whiteTranslations));
  1110.     XtSetValues(whiteTimerWidget, &args[0], 1);
  1111.     XtSetArg(args[0], XtNtranslations,
  1112.          XtParseTranslationTable(blackTranslations));
  1113.     XtSetValues(blackTimerWidget, &args[0], 1);
  1114.     
  1115.     XtAddEventHandler(boardWidget, ExposureMask | ButtonPressMask
  1116.               | ButtonReleaseMask | Button1MotionMask | KeyPressMask,
  1117.               False, (XtEventHandler) EventProc, NULL);
  1118.     
  1119.     sprintf(buf, "xboard version %s, patchlevel %d", VERSION, PATCHLEVEL);
  1120.     
  1121.     /*
  1122.      * If there is to be a machine match, set it up.
  1123.      */
  1124.     if (matchMode != MatchFalse){
  1125.     if (appData.noChessProgram) {
  1126.         fprintf(stderr,
  1127.             "%s: can't have a match with no chess programs!\n",
  1128.             programName);
  1129.         exit(1);
  1130.     }
  1131.     DisplayMessage(buf);
  1132.     TwoMachinesProc(NULL, NULL, NULL, NULL);
  1133.     }
  1134.     else {
  1135.     Reset(True);
  1136.     DisplayMessage(buf);
  1137.     if (*appData.loadGameFile != NULLCHAR)
  1138.       LoadGame(appData.loadGameFile);
  1139.     else if (*appData.loadPositionFile != NULLCHAR)
  1140.       LoadPosition(appData.loadPositionFile);
  1141.     }
  1142.     
  1143.     XtAppMainLoop(appContext);
  1144. }
  1145.  
  1146. /*
  1147.  * Establish will establish a contact to a remote host.port.
  1148.  * Returns 0 if okay, -1 if not.
  1149.  */
  1150. int establish(host, port)
  1151.      char *host;
  1152.      int port;
  1153. {
  1154.     int s;
  1155.     char str[100];
  1156.     int to_prog[2], from_prog[2];
  1157.     struct sockaddr_in sa;
  1158.     struct hostent     *hp;
  1159.     unsigned short uport;
  1160. #if defined(SYSTEM_FIVE) || defined(SYSV)
  1161.     char *pty_name;
  1162. #endif
  1163.     
  1164.     if (appData.useTelnet || (*appData.gateway != NULLCHAR)) {
  1165. #if defined(SYSTEM_FIVE) || defined(SYSV)
  1166.     if ((pty_name = PseudoTTY(&to_prog[1])) == NULL) {
  1167.         fprintf(stderr, "%s: can't open pseudo-tty: ", programName);
  1168.         perror("");
  1169.         exit(1);
  1170.     }
  1171.     from_prog[0] = to_prog[1];
  1172.     to_prog[0] = from_prog[1] = open(pty_name, O_RDWR, 0);
  1173. #ifdef SVR4
  1174.     if (ioctl (to_prog[0], I_PUSH, "ptem") == -1 ||
  1175.         ioctl (to_prog[0], I_PUSH, "ldterm") == -1 ||
  1176.         ioctl (to_prog[0], I_PUSH, "ttcompat") == -1) {
  1177.         fprintf(stderr, "%s: can't ioctl pseudo-tty: ", programName);
  1178.         perror("");
  1179.         exit(1);
  1180.     }
  1181. #endif
  1182. #else
  1183.     pipe(to_prog);
  1184.     pipe(from_prog);
  1185. #endif
  1186.     if ((telnetPID = fork()) == 0) {
  1187.         dup2(to_prog[0], 0);
  1188.         dup2(from_prog[1], 1);
  1189.         close(to_prog[0]);
  1190.         close(to_prog[1]);
  1191.         close(from_prog[0]);
  1192.         close(from_prog[1]);
  1193.         dup2(1, fileno(stderr)); /* force stderr to the pipe */
  1194.         
  1195.         uport = (unsigned short) port;
  1196.         sprintf(str, "%d", uport);
  1197.         if (*appData.gateway != NULLCHAR) {
  1198.         execlp(appData.remoteShell, appData.remoteShell,
  1199.                appData.gateway, appData.telnetProgram,
  1200.                host, str, (char *) NULL);
  1201.         } else {
  1202.         execlp(appData.telnetProgram, appData.telnetProgram,
  1203.                host, str, (char *) NULL);
  1204.         }
  1205.         perror(appData.telnetProgram);
  1206.         exit(1);
  1207.     }
  1208.     close(to_prog[0]);
  1209.     close(from_prog[1]);
  1210.     ics_input = from_prog[0];
  1211.     ics_output = to_prog[1];
  1212.     } else {
  1213. #ifdef SVR4
  1214.         memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
  1215. #else                /*!SVR4*/
  1216.         bzero((char *) &sa, sizeof(struct sockaddr_in));
  1217. #endif
  1218.     if (!(hp = gethostbyname(host))) {
  1219.         int b0, b1, b2, b3;
  1220.         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
  1221.         hp = (struct hostent *) calloc(1, sizeof(struct hostent));
  1222.         hp->h_addrtype = AF_INET;
  1223.         hp->h_length = 4;
  1224.         hp->h_addr_list = (char **) calloc(2, sizeof(char *));
  1225.         hp->h_addr_list[0] = (char *) malloc(4);
  1226.         hp->h_addr_list[0][0] = b0;
  1227.         hp->h_addr_list[0][1] = b1;
  1228.         hp->h_addr_list[0][2] = b2;
  1229.         hp->h_addr_list[0][3] = b3;
  1230.         } else {
  1231.         fprintf(stderr, "%s: could not gethostbyname %s\n",
  1232.             programName, host);
  1233.         return(-1);
  1234.         }
  1235.     }
  1236.     sa.sin_family = hp->h_addrtype;
  1237.     uport = (unsigned short) port;
  1238.     sa.sin_port = htons(uport);
  1239.     
  1240. #ifdef SVR4
  1241.         memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
  1242. #else                /*!SVR4*/
  1243.         bcopy(hp->h_addr, (char *) &sa.sin_addr, hp->h_length);
  1244. #endif
  1245.  
  1246.     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
  1247.         fprintf(stderr, "%s: could not get socket\n", programName);
  1248.         return(-1);
  1249.     }
  1250.     if (connect(s, (struct sockaddr *) &sa, 
  1251.                 sizeof(struct sockaddr_in)) < 0) {
  1252.         fprintf(stderr, "%s: could not bind socket\n", programName);
  1253.         return(-1);
  1254.     }
  1255.     ics_input = ics_output = s;
  1256.     }
  1257.     return(0);
  1258. }
  1259.  
  1260. void read_from_player(client_data, file_num, id)
  1261.      caddr_t client_data;
  1262.      int *file_num;
  1263.      XtInputId *id;
  1264. {
  1265. #define BUF_SIZE 1024
  1266.     int s = *file_num;
  1267.     static char buf[BUF_SIZE];
  1268.     int buf_len, tmp_len;
  1269.     int out_len = 0;
  1270.     
  1271.     buf_len = read(s, buf, BUF_SIZE);
  1272.     
  1273.     if (buf_len > 0) {
  1274.     while (out_len < buf_len) {
  1275.         tmp_len = write(ics_output, &buf[out_len], buf_len - out_len);
  1276.         if (tmp_len == -1) {
  1277.         fprintf(stderr, "%s: error writing to ICS: ", programName);
  1278.         perror("");
  1279.         exit(1);
  1280.         }
  1281.         out_len += tmp_len;
  1282.     }
  1283.     } else {
  1284.     fprintf(stderr, "%s: got end of file from keyboard\n", programName);
  1285.     if (telnetPID != 0) {
  1286.         if (kill(telnetPID, SIGTERM) == 0)
  1287.           wait((union wait *) 0);
  1288.     }
  1289.     exit(0);
  1290.     }
  1291. }
  1292.  
  1293. void SendToICS(s)
  1294.      char *s;
  1295. {
  1296.     int i, j, tmp;
  1297.     
  1298.     i = strlen(s);
  1299.     j = 0;
  1300.     
  1301.     if (appData.debugMode)
  1302.       fprintf(stderr, "Sending to ICS: %s", s);
  1303.  
  1304.     while (j < i) {
  1305.     tmp = write(ics_output, &s[j], i - j);
  1306.     if (tmp == -1) {
  1307.         fprintf(stderr, "%s: error writing to ICS: ", programName);
  1308.         perror("");
  1309.         exit(1);
  1310.     }
  1311.     j += tmp;
  1312.     }
  1313. }
  1314.  
  1315.  
  1316. static int leftover_start = 0, leftover_len = 0;
  1317. static char star_match[8][256];
  1318.  
  1319. /* Test whether pattern is present at &buf[*index]; if so, return True,
  1320.    advance *index beyond it, and set leftover_start to the new value of
  1321.    *index; else return False.  If pattern contains the character '*', it
  1322.    matches any sequence of characters not containing '\r', '\n', or the
  1323.    character following the '*' (if any), and the matched sequence(s) are
  1324.    copied into star_match.  The pattern must not contain '\r' or '\n'.
  1325. */
  1326. Boolean looking_at(buf, index, pattern)
  1327.      char *buf;
  1328.      int *index;
  1329.      char *pattern;
  1330. {
  1331.     char *bufp = &buf[*index], *patternp = pattern;
  1332.     int star_count = 0;
  1333.     char *matchp = star_match[0];
  1334.     
  1335.     for (;;) {
  1336.     if (*patternp == NULLCHAR) {
  1337.         *index = leftover_start = bufp - buf;
  1338.         *matchp = NULLCHAR;
  1339.         return True;
  1340.     }
  1341.     if (*bufp == NULLCHAR) return False;
  1342.     if (*patternp == '*') {
  1343.         if (*bufp == *(patternp + 1)) {
  1344.         *matchp = NULLCHAR;
  1345.         matchp = star_match[++star_count];
  1346.         patternp += 2;
  1347.         bufp++;
  1348.         continue;
  1349.         } else if (*bufp == '\n' || *bufp == '\r') {
  1350.         patternp++;
  1351.         if (*patternp == NULLCHAR)
  1352.           continue;
  1353.         else
  1354.           return False;
  1355.         } else {
  1356.         *matchp++ = *bufp++;
  1357.         continue;
  1358.         }
  1359.     }
  1360.     if (*patternp != *bufp) return False;
  1361.     patternp++;
  1362.     bufp++;
  1363.     }
  1364. }
  1365.  
  1366.  
  1367. void read_from_ics(client_data, file_num, id)
  1368.      caddr_t client_data;
  1369.      int *file_num;
  1370.      XtInputId *id;
  1371. {
  1372. #define BUF_SIZE 1024
  1373. #define BOARD 1
  1374. #define MOVES 2
  1375.     
  1376.     static int started = 0;
  1377.     static char parse[20000];
  1378.     static int  parse_pos;
  1379.     static char buf[BUF_SIZE + 1];
  1380.     
  1381.     char str[500];
  1382.     int i, oldi;
  1383.     int buf_len;
  1384.     int next_out;
  1385.     
  1386.     /* If last read ended with a partial line that we couldn't parse,
  1387.        prepend it to the new read and try again. */
  1388.     if (leftover_len > 0) {
  1389.     for (i=0; i<leftover_len; i++)
  1390.       buf[i] = buf[leftover_start + i];
  1391.     }
  1392.     buf_len = read(ics_input, &buf[leftover_len], BUF_SIZE - leftover_len);
  1393.     next_out = leftover_len;
  1394.     
  1395.     if (buf_len > 0) {
  1396.     buf_len += leftover_len;
  1397.     leftover_start = 0;
  1398.     buf[buf_len] = NULLCHAR;
  1399.     
  1400.     i = 0;
  1401.     while (i < buf_len) {
  1402.         
  1403.         /* Skip over what people say */
  1404.         if (looking_at(buf, &i, "shouts: *") ||
  1405.         looking_at(buf, &i, "tells you: *") ||
  1406.         looking_at(buf, &i, "says: *") ||
  1407.         looking_at(buf, &i, "whispers: *") ||
  1408.         looking_at(buf, &i, "kibitzes: *")) {
  1409.         continue;
  1410.         }
  1411.  
  1412.         if (looking_at(buf, &i,
  1413.                "a   b   c   d   e   f   g   h") || 
  1414.         looking_at(buf, &i,
  1415.                "h   g   f   e   d   c   b   a")) {
  1416.         /* End of board style 1 */
  1417.         SendToICS("style 8\n");
  1418.                 SendToICS("refresh\n");
  1419.         continue;
  1420.         }
  1421.         
  1422.         oldi = i;
  1423.         if (looking_at(buf, &i, "#@#")) {
  1424.         started = BOARD;
  1425.         parse_pos = 0;
  1426.         fwrite(&buf[next_out], oldi - next_out, 1, stdout);
  1427.         continue;
  1428.         }
  1429.         
  1430.         if (started == BOARD && looking_at(buf, &i, "@#@")) {
  1431.         /* Board read is done */
  1432.         started = 0;
  1433.         next_out = i;
  1434.         parse[parse_pos] = NULLCHAR;
  1435.         
  1436.         /* Parse and display the board */
  1437.         ParseBoard8(parse);
  1438.         
  1439.         ics_user_moved = 0;
  1440.         continue;
  1441.         }
  1442.         
  1443.         oldi = i;
  1444.         if ((ics_getting_history || ics_mode == IcsIdle) &&
  1445.         looking_at(buf, &i, "Move ")) {
  1446.         /* Beginning of move list */
  1447.         started = MOVES;
  1448.         parse_pos = 0;
  1449.         fwrite(&buf[next_out], oldi - next_out, 1, stdout);
  1450.         continue;
  1451.         }                
  1452.         
  1453.         if(looking_at(buf, &i, "% ")) {
  1454.         switch (started) {
  1455.           case 0:
  1456.             continue;
  1457.           case BOARD:
  1458.             /* Something went wrong; found a prompt while
  1459.                accumulating a board */
  1460.             started = 0;
  1461.             fprintf(stderr, "%s: error gathering board\n",
  1462.                 programName);
  1463.             continue;
  1464.           case MOVES:
  1465.             started = 0;
  1466.             parse[parse_pos] = NULLCHAR;
  1467.             /* If moves came from oldmoves or moves command
  1468.                while in IcsIdle mode, load them in.  If the
  1469.                game was wild, we are already in "observing -1"
  1470.                mode because we've seen the initial board;
  1471.                otherwise we enter it here.
  1472.             */
  1473.             if (ics_mode == IcsIdle) {
  1474.             Reset(False);
  1475.             ics_mode = IcsObserving;
  1476.             ics_gamenum = -1;
  1477.             ics_getting_history = True;
  1478.             }
  1479.             if (ics_mode == IcsObserving && ics_gamenum == -1) {
  1480.             ParseGameHistory(parse);
  1481.             currentMove = forwardMostMove;
  1482.             DrawPosition(boardWidget, NULL, NULL, NULL);
  1483.             DisplayClocks(ReDisplayTimers);
  1484.             sprintf(str, "%s vs. %s", ics_white, ics_black);
  1485.             DisplayTitle(str);
  1486.             ics_mode = IcsIdle;
  1487.             } else {
  1488.             ParseGameHistory(parse);
  1489.             }
  1490.             DisplayMove(currentMove - 1);
  1491.             SendToICS("\n");  /*kludge: force a prompt*/
  1492.             next_out = i;
  1493.             ics_getting_history = False;
  1494.             continue;
  1495.         }
  1496.         }
  1497.         
  1498.         if (started && i >= leftover_len) {
  1499.         /* Accumulate characters in board
  1500.            or move list*/
  1501.         if (buf[i] != '\r')
  1502.           parse[parse_pos++] = buf[i];
  1503.         }
  1504.         
  1505.         /* Start of game messages.  Mostly we detect start of game
  1506.            when the first board image arrives, but we need to prime
  1507.            the pump for games we're just observing. */
  1508.         if (looking_at(buf, &i, "Adding game * to observation list")) {
  1509.         sprintf(str, "refresh %d\n", atoi(star_match[0]));
  1510.         SendToICS(str);
  1511.         continue;
  1512.         }
  1513.         
  1514.         /* Error messages */
  1515.         if (ics_user_moved) {
  1516.         if (looking_at(buf, &i, "No such command") ||
  1517.             looking_at(buf, &i, "Illegal move") ||
  1518.             looking_at(buf, &i, "Not a legal move") ||
  1519.             looking_at(buf, &i, "Your king is in check") ||
  1520.             looking_at(buf, &i, "It isn't your turn")) {
  1521.             /**** Illegal move ****/
  1522.             ics_user_moved = 0;
  1523.             if (forwardMostMove > backwardMostMove) {
  1524.             currentMove = --forwardMostMove;
  1525.             DisplayMessage("Illegal move");
  1526.             DrawPosition(boardWidget, NULL, NULL, NULL);
  1527.             DisplayClocks(SwitchTimers);
  1528.             }
  1529.             continue;
  1530.         }
  1531.         }
  1532.  
  1533.         if (looking_at(buf, &i, "You and your opponent still have time")) {
  1534.         /* We must have called his flag a little too soon */
  1535.         whiteFlag = blackFlag = False;
  1536.         continue;
  1537.         }
  1538.  
  1539.         /* End-of-game messages */
  1540.         if (looking_at(buf, &i, "{Game * (* vs. *)* * *}")) {
  1541.         /* New style generic game start/end messages */
  1542.         /* star_match[0] is the game number */
  1543.         /*           [1] is the white player's name */
  1544.         /*           [2] is the black player's name */
  1545.         /*           [3] is either ":" or empty (don't care) */
  1546.         /*           [4] is usually the loser's name or a noise word */
  1547.         /*           [5] contains the reason for the game end */
  1548.         int gamenum = atoi(star_match[0]);
  1549.         char *white = star_match[1];
  1550.         char *loser = star_match[4];
  1551.         char *why = star_match[5];
  1552.         
  1553.         if (ics_gamenum != gamenum) continue;
  1554.  
  1555.         if (StrStr(why, "checkmate")) {
  1556.             if (strcmp(loser, white) == 0)
  1557.               GameEnds("Black mates");
  1558.             else
  1559.               GameEnds("White mates");
  1560.         } else if (StrStr(why, "resign")) {
  1561.             if (strcmp(loser, white) == 0)
  1562.               GameEnds("White resigns");
  1563.             else
  1564.               GameEnds("Black resigns");
  1565.         } else if (StrStr(why, "forfeits on time")) {
  1566.             if (strcmp(loser, white) == 0)
  1567.               GameEnds("Black wins on time");
  1568.             else
  1569.               GameEnds("White wins on time");
  1570.         } else if (StrStr(why, "stalemate")) {
  1571.             GameEnds("Stalemate");
  1572.         } else if (StrStr(why, "drawn by mutual agreement")) {
  1573.             GameEnds("Draw agreed");
  1574.         } else if (StrStr(why, "repetition")) {
  1575.             GameEnds("Draw by repetition");
  1576.         } else if (StrStr(why, "50")) {
  1577.             GameEnds("Draw (50 move rule)");
  1578.         } else if (StrStr(why, "neither player has mating")) {
  1579.             GameEnds("Draw (insufficient material)");
  1580.         } else if (StrStr(why, "no material")) {
  1581.             GameEnds("Draw (insufficient material to win on time)");
  1582.         } else if (StrStr(why, "time")) {
  1583.             GameEnds("Draw (both players ran out of time)");
  1584.         } else if (StrStr(why, "disconnected and forfeits")) {
  1585.             /* in this case the word "abuser" preceded the loser */
  1586.             loser = why;
  1587.             why = strchr(loser, ' ');
  1588.             *why++ = NULLCHAR;
  1589.             if (strcmp(loser, white) == 0)
  1590.               GameEnds("Black wins (forfeit)");
  1591.             else
  1592.               GameEnds("White wins (forfeit)");
  1593.         } else if (StrStr(why, "assert")) {
  1594.             /* "loser" is actually the winner in this case */
  1595.             if (strcmp(loser, white) == 0)
  1596.               GameEnds("White asserts a win");
  1597.             else
  1598.               GameEnds("Black asserts a win");
  1599.         } else if (StrStr(why, "aborted")) {
  1600.             DisplayClocks(StopTimers);
  1601.             DisplayMessage("Game aborted");
  1602.             ics_mode = IcsIdle;
  1603.             ics_gamenum = -1;
  1604.             ics_user_moved = False;
  1605.         } else if (StrStr(why, "removed")) {
  1606.             DisplayClocks(StopTimers);
  1607.             DisplayMessage("Game aborted");
  1608.             ics_mode = IcsIdle;
  1609.             ics_gamenum = -1;
  1610.             ics_user_moved = False;
  1611.         } else if (StrStr(why, "adjourn")) {
  1612.             DisplayClocks(StopTimers);
  1613.             DisplayMessage("Game adjourned");
  1614.             ics_mode = IcsIdle;
  1615.             ics_gamenum = -1;
  1616.             ics_user_moved = False;
  1617.         }   
  1618.         continue;
  1619.         }
  1620.  
  1621.         if (looking_at(buf, &i, "Removing game * from observation list")) {
  1622.         if (ics_mode == IcsObserving &&
  1623.             atoi(star_match[0]) == ics_gamenum)
  1624.           {
  1625.               DisplayClocks(StopTimers);
  1626.               ics_mode = IcsIdle;
  1627.               ics_gamenum = -1;
  1628.               ics_user_moved = False;
  1629.           }
  1630.         continue;
  1631.         }
  1632.  
  1633.         /* Advance leftover_start past any newlines we find,
  1634.            so only partial lines can get reparsed */
  1635.         if (looking_at(buf, &i, "\n")) continue;
  1636.         if (looking_at(buf, &i, "\r")) continue;
  1637.         
  1638.         i++;    /* skip unparsed character and loop back */
  1639.     }
  1640.     
  1641.     if (started == 0)
  1642.       fwrite(&buf[next_out], i - next_out, 1, stdout);
  1643.     
  1644.     leftover_len = buf_len - leftover_start;
  1645.     /* if buffer ends with something we couldn't parse,
  1646.        reparse it after appending the next read */
  1647.     
  1648.     } else if (buf_len == 0) {
  1649.     fprintf(stderr, "%s: connection closed by ICS\n", programName);
  1650.     if (telnetPID != 0) {
  1651.         if (kill(telnetPID, SIGTERM) == 0)
  1652.           wait((union wait *) 0);
  1653.     }
  1654.     exit(0);
  1655.     } else {
  1656.     fprintf(stderr, "%s: error reading from ICS: ", programName);
  1657.     perror("");
  1658.     exit(1);
  1659.     }
  1660. }
  1661.  
  1662. /*
  1663.   ICS board style 8 looks like this:
  1664.   
  1665.   #@#000observer        :aaa             :RNBQKBNRPPPPPPPP                                pppppppprnbqkbnr001W39390360003600@#@
  1666.   
  1667.   Information offsets, descriptions and lengths:
  1668.   +3   Game # (3)
  1669.   +6   White's name (16 + ':' = 17)
  1670.   +23  Black's name (16 + ':' = 17)
  1671.   +40  Board  (64)
  1672.   +104 Move # (3)
  1673.   +107 Whose move (1)
  1674.   +108 White Strength (2)
  1675.   +110 Black Strength (2)
  1676.   +112 White Time (5)
  1677.   +117 Black Time (5)
  1678.   +122 Move string (variable
  1679.   A "*" instead of a ":" after the name implies that the person using xboard
  1680.     is playing the game.
  1681.   The move string is either empty or consists of a move followed by
  1682.     elapsed time in parentheses.
  1683.   The pattern defined below doesn't include the #@# and @#@ brackets,
  1684.     and it assumes the board string is null-terminated.  ParseBoard8's
  1685.     caller takes care of this.
  1686.   */
  1687.  
  1688. #define PATTERN "%3d%16s %1c%16s %1c%64c%3d%1c%2d%2d%5d%5d%s %s"
  1689.  
  1690. void ParseBoard8(string)
  1691.      char *string;
  1692.     IcsMode new_ics_mode;
  1693.     int gamenum;
  1694.     int j, k, n, move_num, white_stren, black_stren, white_time, black_time;
  1695.     char playing_white, playing_black, to_play, board_chars[64];
  1696.     char move_str[500], str[500], elapsed_time[500];
  1697.     char black[32], white[32];
  1698.     Board board;
  1699.     
  1700.     if (appData.debugMode)
  1701.       fprintf(stderr, "Parsing board: %s\n", string);
  1702.  
  1703.     move_str[0] = NULLCHAR;
  1704.     elapsed_time[0] = NULLCHAR;
  1705.     
  1706.     n = sscanf(string, PATTERN, &gamenum, white, &playing_white,
  1707.            black, &playing_black, board_chars, &move_num, &to_play,
  1708.            &white_stren, &black_stren, &white_time, &black_time,
  1709.            move_str, elapsed_time);
  1710.     if (n < 12) {
  1711.     fprintf(stderr, "%s: Failed to parse board string: '%s'\n",
  1712.         programName, string);
  1713.     return;
  1714.     }
  1715.  
  1716.     if (playing_white == '*')
  1717.       new_ics_mode = IcsPlayingWhite;
  1718.     else if (playing_black == '*')
  1719.       new_ics_mode = IcsPlayingBlack;
  1720.     else
  1721.       new_ics_mode = IcsObserving;
  1722.     
  1723.     /* Convert the move number to internal form */
  1724.     move_num = (move_num - 1) * 2;
  1725.     if (to_play == 'B') move_num++;
  1726.  
  1727.     /* Deal with initial board display for wild games */
  1728.     if (gamenum == -1) {
  1729.     if (ics_mode == IcsIdle) {
  1730.         /* Initial board for "moves" or "oldmoves" output
  1731.            that we are going to parse.  Go into IcsObserving
  1732.            state with ics_gamenum = -1.
  1733.         */
  1734.         Reset(False);
  1735.         ics_mode = IcsObserving;
  1736.         ics_gamenum = -1;
  1737.         ics_getting_history = True;
  1738.     } else {
  1739.         /* Ignore this board */
  1740.         return;
  1741.     }
  1742.     }
  1743.  
  1744.     /* Take action if this is the first board of a new game */
  1745.     if (gamenum != ics_gamenum) {
  1746.     if (ics_mode == IcsObserving) {
  1747.         /* Error: xboard can't handle two games at once */
  1748.         /* Stop observing the old game */
  1749.         fprintf(stderr, "%s: Aren't you currently observing game %d",
  1750.             programName, ics_gamenum);
  1751.         fprintf(stderr, "?  Attempting to stop observing it.\n");
  1752.         sprintf(str, "observe %d\n", ics_gamenum);
  1753.         SendToICS(str);
  1754.         /* continue as in normal case */
  1755.     } else if (ics_mode != IcsIdle) {
  1756.         /* Error: xboard can't handle two games at once */
  1757.         if (new_ics_mode == IcsObserving) {
  1758.         /* Stop observing the new game */
  1759.         fprintf(stderr, "%s: Aren't you playing a game now?  ",
  1760.             programName);
  1761.         fprintf(stderr, "Attempting to stop observing game %d.\n",
  1762.             gamenum);
  1763.         SendToICS("observe\n");
  1764.         /* ignore this board */
  1765.         return;
  1766.         } else /* new_ics_mode == IcsPlaying(White|Black) */ {
  1767.         /* Playing two games???  ICS supposedly can't do this. */
  1768.         fprintf(stderr, "%s: BUG: playing two games (%d and %d)\n",
  1769.             programName, ics_gamenum, gamenum);
  1770.         /* continue as in normal case, hoping old game is gone */
  1771.         }
  1772.     }
  1773.     /* Normal case (ics_mode == IcsIdle), or error recovered */
  1774.     Reset(False);
  1775.     if (move_num > 0) {
  1776.         /* Need to get game history */
  1777.         ics_getting_history = True;
  1778.         sprintf(str, "moves %d\n", gamenum);
  1779.         SendToICS(str);
  1780.     }
  1781.     }
  1782.  
  1783.     /* Initially flip the board to have black on the bottom iff playing
  1784.        black, but let the user change it with the Flip View button. */
  1785.     if (ics_mode == IcsIdle)
  1786.       flipView = (new_ics_mode == IcsPlayingBlack);
  1787.  
  1788.     /* Update known move number limits */
  1789.     if (ics_mode == IcsIdle) {
  1790.     forwardMostMove = backwardMostMove = currentMove = move_num;
  1791.     } else if (move_num > forwardMostMove) {
  1792.     forwardMostMove = move_num;
  1793.     if (gameMode != PauseGame) 
  1794.       currentMove = move_num;
  1795.     }
  1796.  
  1797.     /* Done with values from previous mode; copy in new ones */
  1798.     ics_mode = new_ics_mode;
  1799.     ics_gamenum = gamenum;
  1800.     strcpy(ics_white, white);
  1801.     strcpy(ics_black, black);
  1802.  
  1803.     /* Parse the board */
  1804.     for (k = 0; k < 8; k++)
  1805.       for (j = 0; j < 8; j++)
  1806.     board[k][j] = CharToPiece(board_chars[k*8 + j]);
  1807.     CopyBoard(boards[move_num], board);
  1808.     if (move_num == 0) {
  1809.     startedFromSetupPosition =
  1810.       !CompareBoards(board, initialPosition);
  1811.     }
  1812.  
  1813.     /* Put the move on the move list, first converting
  1814.        to canonical algebraic form. */
  1815.     if (move_num > 0) {
  1816.     ChessMove move_type;
  1817.     int from_x, from_y, to_x, to_y;
  1818.     char promo_char;
  1819.  
  1820.     if (move_num - 1 < backwardMostMove) {
  1821.         /* We don't know what the board looked like before
  1822.            this move.  Punt. */
  1823.         strcpy(parseList[move_num - 1], move_str);
  1824.     } else {
  1825.         ParseMachineMove(move_str, move_num - 1, &move_type,
  1826.                  &from_x, &from_y, &to_x, &to_y, &promo_char);
  1827.  
  1828.         /* Work around ICS bug: pawn promotion is not indicated,
  1829.            even if underpromoted.  Unfortunately there is no
  1830.            workaround for the same bug when it bites us in
  1831.            ParseGameHistory().
  1832.         */
  1833.         if (move_str[0] == 'P' && (to_y == 0 || to_y == 7))
  1834.           promo_char = ToLower(PieceToChar(board[to_y][to_x]));
  1835.  
  1836.         (void) CoordsToAlgebraic(from_x, from_y, to_x, to_y, promo_char,
  1837.                      move_num - 1, parseList[move_num - 1]);
  1838.         strcat(parseList[move_num - 1], " ");
  1839.         strcat(parseList[move_num - 1], elapsed_time);
  1840.     }
  1841.     }
  1842.     
  1843.     if (move_num < forwardMostMove || ics_gamenum == -1) return;
  1844.  
  1845.     /* Update and display the clocks */
  1846.     timeRemaining[0][move_num] = whiteTimeRemaining = white_time * 1000;
  1847.     timeRemaining[1][move_num] = blackTimeRemaining = black_time * 1000;
  1848.     DisplayClocks(StartTimers);
  1849.  
  1850.     /* Display opponents and material strengths */
  1851.     sprintf(str, "%s (%d) vs. %s (%d)",
  1852.         ics_white, white_stren, ics_black, black_stren);
  1853.     DisplayTitle(str);
  1854.     
  1855.     /* Display the board */
  1856.     if (gameMode != PauseGame) {
  1857.     DrawPosition(boardWidget, NULL, NULL, NULL);
  1858.     DisplayMove(move_num - 1);
  1859.     if (appData.ringBellAfterMoves && !ics_user_moved)
  1860.       putc(BELLCHAR, stderr);
  1861.     }
  1862. }
  1863.  
  1864.  
  1865. #define abs(n) ((n)<0 ? -(n) : (n))
  1866.  
  1867. /*
  1868.  * Find a font that matches "pattern" that is as close as
  1869.  * possible to the targetPxlSize.  Prefer fonts that are k
  1870.  * pixels smaller to fonts that are k pixels larger.  The
  1871.  * pattern must be in the X Consortium standard format, 
  1872.  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
  1873.  * The return value should be freed with XtFree when no
  1874.  * longer needed.
  1875.  */
  1876. char *FindFont(pattern, targetPxlSize)
  1877.      char *pattern;
  1878.      int targetPxlSize;
  1879. {
  1880.     char **fonts, *p, *best;
  1881.     int i, j, nfonts, minerr, err, pxlSize;
  1882.  
  1883.     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
  1884.     if (nfonts < 1) {
  1885.     fprintf(stderr, "%s: No fonts match pattern %s\n",
  1886.         programName, pattern);
  1887.     exit(1);
  1888.     }
  1889.     best = "";
  1890.     minerr = 999999;
  1891.     for (i=0; i<nfonts; i++) {
  1892.     j = 0;
  1893.     p = fonts[i];
  1894.     if (*p != '-') continue;
  1895.     while (j < 7) {
  1896.         if (*p == NULLCHAR) break;
  1897.         if (*p++ == '-') j++;
  1898.     }
  1899.     if (j < 7) continue;
  1900.     pxlSize = atoi(p);
  1901.     if (pxlSize == targetPxlSize) {
  1902.         best = fonts[i];
  1903.         break;
  1904.     }
  1905.     err = pxlSize - targetPxlSize;
  1906.     if (abs(err) < abs(minerr) ||
  1907.         (minerr > 0 && err < 0 && -err == minerr)) {
  1908.         best = fonts[i];
  1909.         minerr = err;
  1910.     }
  1911.     }
  1912.     p = (char *) XtMalloc(strlen(best) + 1);
  1913.     strcpy(p, best);
  1914.     XFreeFontNames(fonts);
  1915.     return p;
  1916. }
  1917.  
  1918. void CreateGCs()
  1919. {
  1920.     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
  1921.       | GCBackground | GCFunction | GCPlaneMask;
  1922.     XGCValues gc_values;
  1923.     
  1924.     gc_values.plane_mask = AllPlanes;
  1925.     gc_values.line_width = LINE_GAP;
  1926.     gc_values.line_style = LineSolid;
  1927.     gc_values.function = GXcopy;
  1928.     
  1929.     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
  1930.     gc_values.background = XBlackPixel(xDisplay, xScreen);
  1931.     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
  1932.     
  1933.     gc_values.background = XWhitePixel(xDisplay, xScreen);
  1934.     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
  1935.     XSetFont(xDisplay, coordGC, coordFontID);
  1936.     
  1937.     if (appData.monoMode) {
  1938.     gc_values.foreground = XWhitePixel(xDisplay, xScreen);
  1939.     gc_values.background = XBlackPixel(xDisplay, xScreen);
  1940.     lightSquareGC = wbPieceGC
  1941.       = XtGetGC(shellWidget, value_mask, &gc_values);
  1942.     
  1943.     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
  1944.     gc_values.background = XWhitePixel(xDisplay, xScreen);
  1945.     darkSquareGC = bwPieceGC
  1946.       = XtGetGC(shellWidget, value_mask, &gc_values);
  1947.     } else {
  1948.     gc_values.foreground = appData.lightSquareColor;
  1949.     gc_values.background = appData.darkSquareColor;
  1950.     lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
  1951.     
  1952.     gc_values.foreground = appData.darkSquareColor;
  1953.     gc_values.background = appData.lightSquareColor;
  1954.     darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
  1955.     
  1956.     gc_values.foreground = appData.whitePieceColor;
  1957.     gc_values.background = appData.darkSquareColor;
  1958.     wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
  1959.     
  1960.     gc_values.foreground = appData.whitePieceColor;
  1961.     gc_values.background = appData.lightSquareColor;
  1962.     wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
  1963.     
  1964.     gc_values.foreground = appData.blackPieceColor;
  1965.     gc_values.background = appData.darkSquareColor;
  1966.     bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
  1967.     
  1968.     gc_values.foreground = appData.blackPieceColor;
  1969.     gc_values.background = appData.lightSquareColor;
  1970.     blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
  1971.     }
  1972. }
  1973.  
  1974. void CreatePieces()
  1975. {
  1976.     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
  1977.                      buffering bug */
  1978.     
  1979.     ReadBitmap(appData.solidPawnBitmap, &solidPawnBitmap,
  1980.            s_p_bits, sm_s_p_bits, xs_s_p_bits);
  1981.     ReadBitmap(appData.solidRookBitmap, &solidRookBitmap,
  1982.            s_r_bits, sm_s_r_bits, xs_s_r_bits);
  1983.     ReadBitmap(appData.solidKnightBitmap, &solidKnightBitmap,
  1984.            s_n_bits, sm_s_n_bits, xs_s_n_bits);
  1985.     ReadBitmap(appData.solidBishopBitmap, &solidBishopBitmap,
  1986.            s_b_bits, sm_s_b_bits, xs_s_b_bits);
  1987.     ReadBitmap(appData.solidQueenBitmap, &solidQueenBitmap,
  1988.            s_q_bits, sm_s_q_bits, xs_s_q_bits);
  1989.     ReadBitmap(appData.solidKingBitmap, &solidKingBitmap,
  1990.            s_k_bits, sm_s_k_bits, xs_s_k_bits);
  1991.     
  1992.     if (appData.monoMode) {
  1993.     ReadBitmap(appData.outlinePawnBitmap, &outlinePawnBitmap,
  1994.            ol_p_bits, sm_ol_p_bits, xs_ol_p_bits);
  1995.     ReadBitmap(appData.outlineRookBitmap, &outlineRookBitmap,
  1996.            ol_r_bits, sm_ol_r_bits, xs_ol_r_bits);
  1997.     ReadBitmap(appData.outlineKnightBitmap, &outlineKnightBitmap,
  1998.            ol_n_bits, sm_ol_n_bits, xs_ol_n_bits);
  1999.     ReadBitmap(appData.outlineBishopBitmap, &outlineBishopBitmap,
  2000.            ol_b_bits, sm_ol_b_bits, xs_ol_b_bits);
  2001.     ReadBitmap(appData.outlineQueenBitmap, &outlineQueenBitmap,
  2002.            ol_q_bits, sm_ol_q_bits, xs_ol_q_bits);
  2003.     ReadBitmap(appData.outlineKingBitmap, &outlineKingBitmap,
  2004.            ol_k_bits, sm_ol_k_bits, xs_ol_k_bits);
  2005.     }
  2006.     
  2007.     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
  2008.                       buffering bug */
  2009. }
  2010.  
  2011. void ReadBitmap(name, pm, large_bits, medium_bits, small_bits)
  2012.      String name;
  2013.      Pixmap *pm;
  2014.      char large_bits[], medium_bits[], small_bits[];
  2015. {
  2016.     int x_hot, y_hot;
  2017.     u_int w, h;
  2018.     
  2019.     if (*name == NULLCHAR ||
  2020.     XReadBitmapFile(xDisplay, xBoardWindow, name,
  2021.             &w, &h, pm, &x_hot, &y_hot) != BitmapSuccess ||
  2022.     w != squareSize || h != squareSize) {
  2023.     switch (boardSize) {
  2024.       case Large:
  2025.         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow,
  2026.                     large_bits, squareSize, squareSize);
  2027.         break;
  2028.       case Medium:
  2029.         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow,
  2030.                     medium_bits, squareSize, squareSize);
  2031.         break;
  2032.       case Small:
  2033.         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow,
  2034.                     small_bits, squareSize, squareSize);
  2035.         break;
  2036.     }
  2037.     }
  2038. }
  2039.  
  2040. void CreateGrid()
  2041. {
  2042.     int i;
  2043.     
  2044.     for (i = 0; i < BOARD_SIZE + 1; i++) {
  2045.     gridSegments[i].x1 = gridSegments[i + BOARD_SIZE + 1].y1 = 0;
  2046.     gridSegments[i].y1 = gridSegments[i].y2
  2047.       = LINE_GAP / 2 + (i * (squareSize + LINE_GAP));
  2048.     gridSegments[i].x2 = LINE_GAP + BOARD_SIZE *
  2049.       (squareSize + LINE_GAP);
  2050.     gridSegments[i + BOARD_SIZE + 1].x1 =
  2051.       gridSegments[i + BOARD_SIZE + 1].x2 = LINE_GAP / 2
  2052.         + (i * (squareSize + LINE_GAP));
  2053.     gridSegments[i + BOARD_SIZE + 1].y2 =
  2054.       BOARD_SIZE * (squareSize + LINE_GAP);
  2055.     }
  2056. }
  2057.  
  2058. void CreatePieceMenus()
  2059. {
  2060.     int i;
  2061.     Widget entry;
  2062.     Arg args[1];
  2063.     ChessSquare selection;
  2064.     
  2065.     XtSetArg(args[0], XtNlabel, "White");
  2066.     whitePieceMenu = XtCreatePopupShell("menuW", simpleMenuWidgetClass,
  2067.                     boardWidget, args, 1);
  2068.     for (i = 0; i < PIECE_MENU_SIZE; i++) {
  2069.     String item = pieceMenuStrings[i];
  2070.     
  2071.     if (strcmp(item, "----") == 0) {
  2072.         entry = XtCreateManagedWidget(item, smeLineObjectClass,
  2073.                       whitePieceMenu, NULL, 0);
  2074.     } else {
  2075.         entry = XtCreateManagedWidget(item, smeBSBObjectClass,
  2076.                       whitePieceMenu, NULL, 0);
  2077.         selection = pieceMenuTranslation[0][i];
  2078.         XtAddCallback(entry, XtNcallback,
  2079.               (XtCallbackProc) PieceMenuSelect,
  2080.               (caddr_t) selection);
  2081.         if (selection == WhitePawn) {
  2082.         XtSetArg(args[0], XtNpopupOnEntry, entry);
  2083.         XtSetValues(whitePieceMenu, args, 1);
  2084.         }
  2085.     }
  2086.     }
  2087.     
  2088.     XtSetArg(args[0], XtNlabel, "Black");
  2089.     blackPieceMenu = XtCreatePopupShell("menuB", simpleMenuWidgetClass,
  2090.                     boardWidget, args, 1);
  2091.     for (i = 0; i < PIECE_MENU_SIZE; i++) {
  2092.     String item = pieceMenuStrings[i];
  2093.     
  2094.     if (strcmp(item, "----") == 0) {
  2095.         entry = XtCreateManagedWidget(item, smeLineObjectClass,
  2096.                       blackPieceMenu, NULL, 0);
  2097.     } else {
  2098.         entry = XtCreateManagedWidget(item, smeBSBObjectClass,
  2099.                       blackPieceMenu, NULL, 0);
  2100.         selection = pieceMenuTranslation[1][i];
  2101.         XtAddCallback(entry, XtNcallback,
  2102.               (XtCallbackProc) PieceMenuSelect,
  2103.               (caddr_t) selection);
  2104.         if (selection == BlackPawn) {
  2105.         XtSetArg(args[0], XtNpopupOnEntry, entry);
  2106.         XtSetValues(blackPieceMenu, args, 1);
  2107.         }
  2108.     }
  2109.     }
  2110.     
  2111.     XtRegisterGrabAction(PieceMenuPopup, True,
  2112.              (unsigned)(ButtonPressMask|ButtonReleaseMask),
  2113.              GrabModeAsync, GrabModeAsync);
  2114. }    
  2115.  
  2116. void PieceMenuPopup(w, event, params, num_params)
  2117.      Widget w;
  2118.      XEvent *event;
  2119.      String *params;
  2120.      Cardinal *num_params;
  2121. {
  2122.     if (event->type != ButtonPress) return;
  2123.     if (gameMode != EditPosition) return;
  2124.     
  2125.     if (((pmFromX = EventToSquare(event->xbutton.x)) < 0) ||
  2126.     ((pmFromY = EventToSquare(event->xbutton.y)) < 0)) {
  2127.     pmFromX = pmFromY = -1;
  2128.     return;
  2129.     }
  2130.     if (flipView)
  2131.       pmFromX = BOARD_SIZE - 1 - pmFromX;
  2132.     else
  2133.       pmFromY = BOARD_SIZE - 1 - pmFromY;
  2134.     
  2135.     XtPopupSpringLoaded(XtNameToWidget(boardWidget, params[0]));
  2136. }
  2137.  
  2138. static void PieceMenuSelect(w, piece, junk)
  2139.      Widget w;
  2140.      ChessSquare piece;
  2141.      caddr_t junk;
  2142. {
  2143.     if (pmFromX < 0 || pmFromY < 0) return;
  2144.     switch (piece) {
  2145.       case ClearBoard:
  2146.     for (pmFromY = 0; pmFromY < BOARD_SIZE; pmFromY++)
  2147.       for (pmFromX = 0; pmFromX < BOARD_SIZE; pmFromX++)
  2148.           boards[0][pmFromY][pmFromX] = EmptySquare;
  2149.     DrawPosition(boardWidget, NULL, NULL, NULL);
  2150.     break;
  2151.     
  2152.       case WhitePlay:    /*not currently on menu*/
  2153.     SetWhiteToPlay(NULL, NULL, NULL, NULL);
  2154.     break;
  2155.     
  2156.       case BlackPlay:    /*not currently on menu*/
  2157.     SetBlackToPlay(NULL, NULL, NULL, NULL);
  2158.     break;
  2159.     
  2160.       default:
  2161.     boards[0][pmFromY][pmFromX] = piece;
  2162.     DrawPosition(boardWidget, NULL, NULL, NULL);
  2163.     break;
  2164.     }
  2165.     XSync(xDisplay, False);
  2166. }
  2167.  
  2168. static void SetWhiteToPlay(w, event, prms, nprms)
  2169.      Widget w;
  2170.      XEvent *event;
  2171.      String *prms;
  2172.      Cardinal *nprms;
  2173. {
  2174.     if (gameMode != EditPosition) return;
  2175.     blackPlaysFirst = False;
  2176.     DisplayClocks(ReDisplayTimers); /* works because currentMove is 0 */
  2177. }
  2178.  
  2179. static void SetBlackToPlay(w, event, prms, nprms)
  2180.      Widget w;
  2181.      XEvent *event;
  2182.      String *prms;
  2183.      Cardinal *nprms;
  2184. {
  2185.     if (gameMode != EditPosition) return;
  2186.     blackPlaysFirst = True;
  2187.     currentMove = 1;    /* kludge */
  2188.     DisplayClocks(ReDisplayTimers);
  2189.     currentMove = 0;
  2190. }
  2191.  
  2192. char PieceToChar(p)
  2193.      ChessSquare p;
  2194. {
  2195.     return pieceToChar[(int) p];
  2196. }
  2197.  
  2198. ChessSquare CharToPiece(c)
  2199.      int c;
  2200. {
  2201.     switch (c) {
  2202.       default:
  2203.       case '.':    return EmptySquare;
  2204.       case 'P':    return WhitePawn;
  2205.       case 'R':    return WhiteRook;
  2206.       case 'N':    return WhiteKnight;
  2207.       case 'B':    return WhiteBishop;
  2208.       case 'Q':    return WhiteQueen;
  2209.       case 'K':    return WhiteKing;
  2210.       case 'p':    return BlackPawn;
  2211.       case 'r':    return BlackRook;
  2212.       case 'n':    return BlackKnight;
  2213.       case 'b':    return BlackBishop;
  2214.       case 'q':    return BlackQueen;
  2215.       case 'k':    return BlackKing;
  2216.     }
  2217. }
  2218.  
  2219. ChessSquare PromoPiece(move_type)
  2220.      ChessMove move_type;
  2221. {
  2222.     switch (move_type) {
  2223.       default:
  2224.     return EmptySquare;
  2225.       case WhitePromotionQueen:
  2226.     return WhiteQueen;
  2227.       case BlackPromotionQueen:
  2228.     return BlackQueen;
  2229.       case WhitePromotionRook:
  2230.     return WhiteRook;
  2231.       case BlackPromotionRook:
  2232.     return BlackRook;
  2233.       case WhitePromotionBishop:
  2234.     return WhiteBishop;
  2235.       case BlackPromotionBishop:
  2236.     return BlackBishop;
  2237.       case WhitePromotionKnight:
  2238.     return WhiteKnight;
  2239.       case BlackPromotionKnight:
  2240.     return BlackKnight;
  2241.     }
  2242. }
  2243.  
  2244.  
  2245. /* Convert coordinates to normal algebraic notation.
  2246.    promoChar must be NULLCHAR or '.' if not a promotion.
  2247.    */
  2248. ChessMove CoordsToAlgebraic(fromX, fromY, toX, toY, promoChar,
  2249.                 currentBoardIndex, out)
  2250.      int fromX, fromY, toX, toY;
  2251.      int promoChar;
  2252.      int currentBoardIndex;
  2253.      char out[MOVE_LEN];
  2254. {
  2255.     ChessSquare piece;
  2256.     ChessMove ret;
  2257.     char *outp = out;
  2258.     int i;
  2259.     
  2260.     if (promoChar == '.') promoChar = NULLCHAR;
  2261.     piece = boards[currentBoardIndex][fromY][fromX];
  2262.     switch (piece) {
  2263.       case WhitePawn:
  2264.       case BlackPawn:
  2265.     /* Pawn move */
  2266.     *outp++ = fromX + 'a';
  2267.     if (fromX == toX) {
  2268.         /* Non-capture; use style "e5" or "e8Q" */
  2269.         *outp++ = toY + '1';
  2270.         *outp++ = ToUpper(promoChar);
  2271.         *outp = NULLCHAR;
  2272.     } else {
  2273.         /* Capture; use style "exd5" or "exd8Q" */
  2274.         *outp++ = 'x';
  2275.         *outp++ = toX + 'a';
  2276.         *outp++ = toY + '1';
  2277.         *outp++ = ToUpper(promoChar);
  2278.         *outp = NULLCHAR;
  2279.     }
  2280.     
  2281.     /* Test if okay by parsing; this notation should parse 
  2282.        unambiguously if the original move was legal.  More
  2283.        code would be needed if we wanted the style "ed" for
  2284.        captures, since that can be ambiguous.
  2285.        */
  2286.     ret = yylexstr(currentBoardIndex, out, (char **) NULL);
  2287.     break;
  2288.     
  2289.       case WhiteKing:
  2290.       case BlackKing:
  2291.     /* Test for castling or ICS wild castling */
  2292.     /* Use style "0-0" (zero-zero) */
  2293.     if (fromY == toY &&
  2294.         fromY == ((piece == WhiteKing) ? 0 : 7) &&
  2295.         ((fromX == 4 && (toX == 2 || toX == 6)) ||
  2296.          (fromX == 3 && (toX == 1 || toX == 5)))) {
  2297.         switch (toX) {
  2298.           case 1:
  2299.           case 6:
  2300.         strcpy(out, "0-0");
  2301.         break;
  2302.           case 2:
  2303.           case 5:
  2304.         strcpy(out, "0-0-0");
  2305.         break;
  2306.         }
  2307.         ret = yylexstr(currentBoardIndex, out, (char **) NULL);
  2308.         break;
  2309.     }
  2310.     /* else fall through */
  2311.     
  2312.       default:
  2313.     /* Piece move */
  2314.     /* First try style "Nf3" or "Nxf7" */
  2315.     *outp++ = ToUpper(PieceToChar(piece));
  2316.     
  2317.     /* Capture? */
  2318.     if(boards[currentBoardIndex][toY][toX] != EmptySquare)
  2319.       *outp++ = 'x';
  2320.     
  2321.     *outp++ = toX + 'a';
  2322.     *outp++ = toY + '1';
  2323.     *outp = NULLCHAR;
  2324.     
  2325.     /* Test if ambiguous */
  2326.     ret = yylexstr(currentBoardIndex, out, (char **) NULL);
  2327.     if (ret != AmbiguousMove) break;
  2328.     
  2329.     /* Try style "Ngf3" or "Nexf7" */
  2330.     for (i=4; i>=1; i--) out[i+1] = out[i];
  2331.     out[1] = fromX + 'a';
  2332.     
  2333.     /* Test if ambiguous */
  2334.     ret = yylexstr(currentBoardIndex, out, (char **) NULL);
  2335.     if (ret != AmbiguousMove) break;
  2336.     
  2337.     /* Try style "N1f3" */
  2338.     out[1] = fromY + '1';
  2339.     
  2340.     /* Test if ambiguous */
  2341.     ret = yylexstr(currentBoardIndex, out, (char **) NULL);
  2342.     if (ret != AmbiguousMove) break;
  2343.     
  2344.     /* Try style "Ng1f3" or "Ne5xf7" */
  2345.     /* Can be needed iff there are 3 or more pieces of the
  2346.        type being moved on the board, due to promotion */
  2347.     for (i=5; i>=2; i--) out[i+1] = out[i];
  2348.     out[1] = fromX + 'a';
  2349.     out[2] = fromY + '1';
  2350.     
  2351.     /* Test if okay */
  2352.     ret = yylexstr(currentBoardIndex, out, (char **) NULL);
  2353.     break; 
  2354.     
  2355.       case EmptySquare:
  2356.     /* Illegal move; use coordinate notation */
  2357.     *outp++ = fromX + 'a';
  2358.     *outp++ = fromY + '1';
  2359.     *outp++ = toX + 'a';
  2360.     *outp++ = toY + '1';
  2361.     *outp++ = ToUpper(promoChar);
  2362.     *outp = NULLCHAR;
  2363.     return BadMove;
  2364.     }
  2365.     
  2366.     switch (ret) {
  2367.       case NormalMove:
  2368.       case WhitePromotionKnight:
  2369.       case WhitePromotionBishop:
  2370.       case WhitePromotionRook:
  2371.       case WhitePromotionQueen:
  2372.       case BlackPromotionKnight:
  2373.       case BlackPromotionBishop:
  2374.       case BlackPromotionRook:
  2375.       case BlackPromotionQueen:
  2376.       case WhiteCapturesEnPassant:
  2377.       case BlackCapturesEnPassant:
  2378.       case WhiteKingSideCastle:
  2379.       case WhiteQueenSideCastle:
  2380.       case BlackKingSideCastle:
  2381.       case BlackQueenSideCastle:
  2382.       case WhiteKingSideCastleWild:
  2383.       case WhiteQueenSideCastleWild:
  2384.       case BlackKingSideCastleWild:
  2385.       case BlackQueenSideCastleWild:
  2386.     if (currentMoveString[0] != fromX + 'a' ||
  2387.         currentMoveString[1] != fromY + '1' ||
  2388.         currentMoveString[2] != toX + 'a' ||
  2389.         currentMoveString[3] != toY + '1' ||
  2390.         (promoChar != NULLCHAR &&
  2391.          currentMoveString[4] != ToLower(promoChar))) {
  2392.         /* Illegal move; use coordinate notation */
  2393.         outp = out;
  2394.         *outp++ = fromX + 'a';
  2395.         *outp++ = fromY + '1';
  2396.         *outp++ = toX + 'a';
  2397.         *outp++ = toY + '1';
  2398.         *outp++ = ToUpper(promoChar);
  2399.         *outp = NULLCHAR;
  2400.         return BadMove;
  2401.     }
  2402.     else
  2403.       return ret;
  2404.     
  2405.       default:
  2406.     /* Illegal move; use coordinate notation */
  2407.     outp = out;
  2408.     *outp++ = fromX + 'a';
  2409.     *outp++ = fromY + '1';
  2410.     *outp++ = toX + 'a';
  2411.     *outp++ = toY + '1';
  2412.     *outp++ = ToUpper(promoChar);
  2413.     *outp = NULLCHAR;
  2414.     return BadMove;
  2415.     }
  2416. }
  2417.  
  2418.  
  2419. /*
  2420.  * If the user selects on a border boundary, return -1; if off the board,
  2421.  *   return -2.  Otherwise map the event coordinate to the square.
  2422.  */
  2423. int EventToSquare(x)
  2424.      int x;
  2425. {
  2426.     if (x <= 0) 
  2427.       return -2;
  2428.     if (x < LINE_GAP)
  2429.       return -1;
  2430.     x -= LINE_GAP;
  2431.     if ((x % (squareSize + LINE_GAP)) >= squareSize)
  2432.       return -1;
  2433.     x /= (squareSize + LINE_GAP);
  2434.     if (x >= BOARD_SIZE)
  2435.       return -2;
  2436.     return x;
  2437. }
  2438.  
  2439. void DrawSquare(row, column, piece)
  2440.      int row, column;
  2441.      ChessSquare piece;
  2442. {
  2443.     int square_color, x, y, direction, font_ascent, font_descent;
  2444.     char string[2];
  2445.     XCharStruct overall;
  2446.     
  2447.     if (flipView) {
  2448.     x = LINE_GAP + ((BOARD_SIZE-1)-column) * 
  2449.       (squareSize + LINE_GAP);
  2450.     y = LINE_GAP + row * (squareSize + LINE_GAP);
  2451.     } else {
  2452.     x = LINE_GAP + column * (squareSize + LINE_GAP);
  2453.     y = LINE_GAP + ((BOARD_SIZE-1)-row) * 
  2454.       (squareSize + LINE_GAP);
  2455.     }
  2456.     
  2457.     square_color = ((column + row) % 2) == 1;
  2458.     
  2459.     if (piece == EmptySquare)
  2460.       XFillRectangle(xDisplay, xBoardWindow,
  2461.              square_color ? lightSquareGC : darkSquareGC,
  2462.              x, y, squareSize, squareSize);
  2463.     else if (appData.monoMode) {
  2464.     if (square_color)
  2465.       XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
  2466.              ? *pieceToOutline[(int) piece]
  2467.              : *pieceToSolid[(int) piece],
  2468.              xBoardWindow, bwPieceGC, 0, 0,
  2469.              squareSize, squareSize, x, y, 1);
  2470.     else
  2471.       XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
  2472.              ? *pieceToSolid[(int) piece]
  2473.              : *pieceToOutline[(int) piece],
  2474.              xBoardWindow, wbPieceGC, 0, 0,
  2475.              squareSize, squareSize, x, y, 1);
  2476.     } else {
  2477.     if (square_color)
  2478.       XCopyPlane(xDisplay, *pieceToSolid[(int) piece],
  2479.              xBoardWindow, (int) piece < (int) BlackPawn
  2480.              ? wlPieceGC : blPieceGC, 0, 0,
  2481.              squareSize, squareSize, x, y, 1);
  2482.     else
  2483.       XCopyPlane(xDisplay, *pieceToSolid[(int) piece],
  2484.              xBoardWindow, (int) piece < (int) BlackPawn
  2485.              ? wdPieceGC : bdPieceGC, 0, 0,
  2486.              squareSize, squareSize, x, y, 1);
  2487.     }
  2488.     string[1] = NULLCHAR;
  2489.     if (appData.showCoords && row == (flipView ? 7 : 0)) {
  2490.     string[0] = 'a' + column;
  2491.     XTextExtents(coordFontStruct, string, 1, &direction, 
  2492.              &font_ascent, &font_descent, &overall);
  2493.     if (appData.monoMode) {
  2494.         XDrawImageString(xDisplay, xBoardWindow, coordGC,
  2495.                  x + squareSize - overall.width - 2, 
  2496.                  y + squareSize - font_descent - 1, string, 1);
  2497.     } else {
  2498.         XDrawString(xDisplay, xBoardWindow, coordGC,
  2499.             x + squareSize - overall.width - 2, 
  2500.             y + squareSize - font_descent - 1, string, 1);
  2501.     }
  2502.     }
  2503.     if (appData.showCoords && column == (flipView ? 7 : 0)) {
  2504.     string[0] = '1' + row;
  2505.     XTextExtents(coordFontStruct, string, 1, &direction, 
  2506.              &font_ascent, &font_descent, &overall);
  2507.     if (appData.monoMode) {
  2508.         XDrawImageString(xDisplay, xBoardWindow, coordGC,
  2509.                  x + 2, y + font_ascent + 1, string, 1);
  2510.     } else {
  2511.         XDrawString(xDisplay, xBoardWindow, coordGC,
  2512.             x + 2, y + font_ascent + 1, string, 1);
  2513.     }        
  2514.     }   
  2515. }
  2516.  
  2517. void EventProc(widget, unused, event)
  2518.      Widget widget;
  2519.      caddr_t unused;
  2520.      XEvent *event;
  2521. {
  2522.     if (event->type == MappingNotify) {
  2523.     XRefreshKeyboardMapping((XMappingEvent *) event);
  2524.     return;
  2525.     }
  2526.     
  2527.     if (!XtIsRealized(widget))
  2528.       return;
  2529.     
  2530.     if ((event->type == ButtonPress) || (event->type == ButtonRelease))
  2531.       if (event->xbutton.button != Button1)
  2532.     return;
  2533.     
  2534.     switch (event->type) {
  2535.       case Expose:
  2536.     DrawPosition(widget, event, NULL, NULL);
  2537.     break;
  2538.       default:
  2539.     return;
  2540.     }
  2541. }
  2542.  
  2543. /*
  2544.  * event handler for redrawing the board
  2545.  */
  2546. void DrawPosition(w, event, prms, nprms)
  2547.      Widget w;
  2548.      XEvent *event;
  2549.      String *prms;
  2550.      Cardinal *nprms;
  2551. {
  2552.     Arg args[1];
  2553.     int i, j;
  2554.     static Board lastBoard;
  2555.     static int lastBoardValid = 0;
  2556.     static int lastFlipView = 0;
  2557.     
  2558.     if (!appData.Iconic){
  2559.     XtSetArg(args[0], XtNiconic, False);
  2560.     XtSetValues(shellWidget, args, 1);
  2561.     }
  2562.     
  2563.     /*
  2564.      * It would be simpler to clear the window with XClearWindow()
  2565.      * but this causes a very distracting flicker.
  2566.      */
  2567.     
  2568.     if (event == NULL && lastBoardValid && lastFlipView == flipView) {
  2569.     for (i = 0; i < BOARD_SIZE; i++)
  2570.       for (j = 0; j < BOARD_SIZE; j++)
  2571.         if (boards[currentMove][i][j] != lastBoard[i][j])
  2572.           DrawSquare(i, j, boards[currentMove][i][j]);
  2573.     } else {
  2574.     XDrawSegments(xDisplay, xBoardWindow, lineGC,
  2575.               gridSegments, (BOARD_SIZE + 1) * 2);
  2576.     
  2577.     for (i = 0; i < BOARD_SIZE; i++)
  2578.       for (j = 0; j < BOARD_SIZE; j++)
  2579.         DrawSquare(i, j, boards[currentMove][i][j]);
  2580.     }
  2581.     
  2582.     CopyBoard(lastBoard, boards[currentMove]);
  2583.     lastBoardValid = 1;
  2584.     lastFlipView = flipView;
  2585.     
  2586.     XSync(xDisplay, False);
  2587. }
  2588.  
  2589. void InitPosition(redraw)
  2590.      int redraw;
  2591. {
  2592.     currentMove = forwardMostMove = backwardMostMove = 0;
  2593.     CopyBoard(boards[0], initialPosition);
  2594.     if (redraw)
  2595.       DrawPosition(boardWidget, NULL, NULL, NULL);
  2596. }
  2597.  
  2598. void CopyBoard(to, from)
  2599.      Board to, from;
  2600. {
  2601.     int i, j;
  2602.     
  2603.     for (i = 0; i < BOARD_SIZE; i++)
  2604.       for (j = 0; j < BOARD_SIZE; j++)
  2605.     to[i][j] = from[i][j];
  2606. }
  2607.  
  2608. Boolean CompareBoards(board1, board2)
  2609.      Board board1, board2;
  2610. {
  2611.     int i, j;
  2612.     
  2613.     for (i = 0; i < BOARD_SIZE; i++)
  2614.       for (j = 0; j < BOARD_SIZE; j++) {
  2615.       if (board1[i][j] != board2[i][j])
  2616.         return FALSE;
  2617.     }
  2618.     return TRUE;
  2619. }
  2620.  
  2621. void SendCurrentBoard(fp)
  2622.      FILE *fp;
  2623. {
  2624.     SendBoard(fp, boards[currentMove]);
  2625. }
  2626.  
  2627. void SendBoard(fp, board)
  2628.      FILE *fp;
  2629.      Board board;
  2630. {
  2631.     char message[MSG_SIZ];
  2632.     ChessSquare *bp;
  2633.     int i, j;
  2634.     
  2635.     SendToProgram("edit\n", fp);
  2636.     SendToProgram("#\n", fp);
  2637.     for (i = BOARD_SIZE - 1; i >= 0; i--) {
  2638.     bp = &board[i][0];
  2639.     for (j = 0; j < BOARD_SIZE; j++, bp++) {
  2640.         if ((int) *bp < (int) BlackPawn) {
  2641.         sprintf(message, "%c%c%c\n", PieceToChar(*bp), 
  2642.             'a' + j, '1' + i);
  2643.         SendToProgram(message, fp);
  2644.         }
  2645.     }
  2646.     }
  2647.     
  2648.     SendToProgram("c\n", fp);
  2649.     for (i = BOARD_SIZE - 1; i >= 0; i--) {
  2650.     bp = &board[i][0];
  2651.     for (j = 0; j < BOARD_SIZE; j++, bp++) {
  2652.         if (((int) *bp != (int) EmptySquare)
  2653.         && ((int) *bp >= (int) BlackPawn)) {
  2654.         sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
  2655.             'a' + j, '1' + i);
  2656.         SendToProgram(message, fp);
  2657.         }
  2658.     }
  2659.     }
  2660.     
  2661.     SendToProgram(".\n", fp);
  2662. }
  2663.  
  2664. /*
  2665.  * event handler for parsing user moves
  2666.  */
  2667. void HandleUserMove(w, event, prms, nprms)
  2668.      Widget w;
  2669.      XEvent *event;
  2670.      String *prms;
  2671.      Cardinal *nprms;
  2672. {
  2673.     ChessMove move_type;
  2674.     ChessSquare from_piece;
  2675.     int to_x, to_y;
  2676.     
  2677.     if ((w != boardWidget) || (matchMode != MatchFalse))
  2678.       return;
  2679.     
  2680.     if (promotionUp) {
  2681.     XtPopdown(promotionShell);
  2682.     XtDestroyWidget(promotionShell);
  2683.     promotionUp = False;
  2684.     fromX = fromY = -1;
  2685.     }
  2686.     
  2687.     switch (gameMode) {
  2688.       case EndOfGame:
  2689.       case PlayFromGameFile:
  2690.       case TwoMachinesPlay:
  2691.     return;
  2692.       case MachinePlaysWhite:
  2693.     if (WhiteOnMove(forwardMostMove)) {
  2694.         DisplayMessage("It is not your turn");
  2695.         return;
  2696.     }
  2697.     break;
  2698.       case MachinePlaysBlack:
  2699.     if (!WhiteOnMove(forwardMostMove)) {
  2700.         DisplayMessage("It is not your turn");
  2701.         return;
  2702.     }
  2703.     break;
  2704.       case ForceMoves:
  2705.     if (appData.icsActive) {
  2706.         if (ics_user_moved ||
  2707.         ics_mode == IcsObserving ||
  2708.         ics_mode == IcsIdle)
  2709.           return;
  2710.     } else {
  2711.         forwardMostMove = currentMove;
  2712.     }
  2713.     break;
  2714.       default:
  2715.     break;
  2716.     }
  2717.     
  2718.     if (currentMove != forwardMostMove) {
  2719.     DisplayMessage("Displayed position is not current");
  2720.     return;
  2721.     }
  2722.     
  2723.     switch (event->type) {
  2724.       case ButtonPress:
  2725.     if ((fromX >= 0) || (fromY >= 0))
  2726.       return;
  2727.     if (((fromX = EventToSquare(event->xbutton.x)) < 0) ||
  2728.         ((fromY = EventToSquare(event->xbutton.y)) < 0)) {
  2729.         fromX = fromY = -1;
  2730.         return;
  2731.     }
  2732.     if (flipView)
  2733.       fromX = BOARD_SIZE - 1 - fromX;
  2734.     else
  2735.       fromY = BOARD_SIZE - 1 - fromY;
  2736.     break;
  2737.     
  2738.       case ButtonRelease:
  2739.     if ((fromX < 0) || (fromY < 0)) return;
  2740.     
  2741.     if (((to_x = EventToSquare(event->xbutton.x)) < 0)
  2742.         || ((to_y = EventToSquare(event->xbutton.y)) < 0)) {
  2743.         if (gameMode == EditPosition && (to_x == -2 || to_y == -2)) {
  2744.         boards[0][fromY][fromX] = EmptySquare;
  2745.         DrawPosition(boardWidget, NULL, NULL, NULL);
  2746.         XSync(xDisplay, False);
  2747.         }
  2748.         fromX = fromY = -1;
  2749.         return;
  2750.     }
  2751.     if (flipView)
  2752.       to_x = BOARD_SIZE - 1 - to_x;
  2753.     else
  2754.       to_y = BOARD_SIZE - 1 - to_y;
  2755.     
  2756.     if ((fromX == to_x) && (fromY == to_y)) {
  2757.         fromX = fromY = -1;
  2758.         return;
  2759.     }
  2760.     
  2761.     if (gameMode == EditPosition) {
  2762.         boards[0][to_y][to_x] = boards[0][fromY][fromX];
  2763.         boards[0][fromY][fromX] = EmptySquare;
  2764.         fromX = fromY = -1;
  2765.         DrawPosition(boardWidget, NULL, NULL, NULL);
  2766.         XSync(xDisplay, False);
  2767.         return;
  2768.     }
  2769.     
  2770.     from_piece = boards[currentMove][fromY][fromX];
  2771.     if (from_piece == EmptySquare) {
  2772.         fromX = fromY = -1;
  2773.         return;
  2774.     }
  2775.     if (WhiteOnMove(currentMove)) {
  2776.         if ((int)from_piece < (int)WhitePawn ||
  2777.         (int)from_piece > (int)WhiteKing) {
  2778.         DisplayMessage("It is White's turn");
  2779.         fromX = fromY = -1;
  2780.         return;
  2781.         }
  2782.     } else {
  2783.         if ((int)from_piece < (int)BlackPawn ||
  2784.         (int)from_piece > (int)BlackKing) {
  2785.         DisplayMessage("It is Black's turn");
  2786.         fromX = fromY = -1;
  2787.         return;
  2788.         }
  2789.     }
  2790.     if (((from_piece == WhitePawn && to_y == 7) ||
  2791.          (from_piece == BlackPawn && to_y == 0))) {
  2792.         PromotionPopUp(from_piece, to_x, to_y);
  2793.         return;
  2794.     }
  2795.     move_type = LegalityTest(WhiteOnMove(currentMove),
  2796.                  boards[currentMove], fromY, fromX,
  2797.                  to_y, to_x, NULLCHAR);
  2798.     if (move_type == BadMove) {
  2799.         DisplayMessage("Illegal move");
  2800.         fromX = fromY = -1;
  2801.         return;
  2802.     }
  2803.     MakeMove(&move_type, fromX, fromY, to_x, to_y);
  2804.     FinishUserMove(move_type, to_x, to_y);
  2805.     break;
  2806.     }
  2807. }
  2808.  
  2809. void FinishUserMove(move_type, to_x, to_y)
  2810.      ChessMove move_type;
  2811.      int to_x, to_y;
  2812. {
  2813.     char user_move[MSG_SIZ];
  2814.     char promoChar;
  2815.     
  2816.     if (appData.icsActive) {
  2817.     sprintf(user_move, "%c%c%c%c\n",
  2818.         'a' + fromX, '1' + fromY, 'a' + to_x, '1' + to_y);
  2819.     switch (move_type) {
  2820.       default:
  2821.         fprintf(stderr, "%s: internal error; bad move_type\n",
  2822.             programName);
  2823.         break;
  2824.       case WhiteKingSideCastle:
  2825.       case BlackKingSideCastle:
  2826.       case WhiteQueenSideCastleWild:
  2827.       case BlackQueenSideCastleWild:
  2828.         sprintf(user_move, "o-o\n");
  2829.         break;
  2830.       case WhiteQueenSideCastle:
  2831.       case BlackQueenSideCastle:
  2832.       case WhiteKingSideCastleWild:
  2833.       case BlackKingSideCastleWild:
  2834.         sprintf(user_move, "o-o-o\n");
  2835.         break;
  2836.       case WhitePromotionQueen:
  2837.       case BlackPromotionQueen:
  2838.         SendToICS("promote queen\n");
  2839.         break;
  2840.       case WhitePromotionRook:
  2841.       case BlackPromotionRook:
  2842.         SendToICS("promote rook\n");
  2843.         break;
  2844.       case WhitePromotionBishop:
  2845.       case BlackPromotionBishop:
  2846.         SendToICS("promote bishop\n");
  2847.         break;
  2848.       case WhitePromotionKnight:
  2849.       case BlackPromotionKnight:
  2850.         SendToICS("promote knight\n");
  2851.         break;
  2852.       case NormalMove:
  2853.       case WhiteCapturesEnPassant:
  2854.       case BlackCapturesEnPassant:
  2855.         break;
  2856.     }
  2857.     SendToICS(user_move);
  2858.     ics_user_moved = 1;
  2859.     } else {
  2860.     promoChar = ToLower(PieceToChar(PromoPiece(move_type)));
  2861.     if (promoChar == '.')
  2862.       sprintf(user_move, "%c%c%c%c\n",
  2863.           'a' + fromX, '1' + fromY, 'a' + to_x, '1' + to_y);
  2864.     else
  2865.       sprintf(user_move, "%c%c%c%c%c\n",
  2866.           'a' + fromX, '1' + fromY, 'a' + to_x, '1' + to_y,
  2867.           promoChar);
  2868.     
  2869.     Attention(firstProgramPID);
  2870.     if (firstSendTime)
  2871.       SendTimeRemaining(toFirstProgFP);
  2872.     SendToProgram(user_move, toFirstProgFP);
  2873.     }
  2874.     
  2875.     fromX = fromY = -1;
  2876.     
  2877.     strcpy(moveList[currentMove - 1], user_move);
  2878.     
  2879.     /* A user move restarts a paused game*/
  2880.     if (gameMode == PauseGame)
  2881.       PauseProc(NULL, NULL, NULL, NULL);
  2882.     
  2883.     switch (gameMode) {
  2884.       case ForceMoves:
  2885.     break;
  2886.       case PlayFromGameFile:
  2887.     ForceProc(NULL, NULL, NULL, NULL);
  2888.     break;
  2889.       case BeginningOfGame:
  2890.     if (appData.noChessProgram)
  2891.       lastGameMode = gameMode = ForceMoves;
  2892.     else
  2893.       lastGameMode = gameMode = MachinePlaysBlack;
  2894.     ModeHighlight();
  2895.     break;
  2896.       case MachinePlaysBlack:
  2897.       case MachinePlaysWhite:
  2898.       default:
  2899.     break;
  2900.     }
  2901. }
  2902.  
  2903. /* Parser for moves from gnuchess or ICS */
  2904. void ParseMachineMove(machine_move, move_num,
  2905.               move_type, from_x, from_y, to_x, to_y, promo_char)
  2906.      char *machine_move;
  2907.      int move_num;
  2908.      ChessMove *move_type;
  2909.      int *from_x, *from_y, *to_x, *to_y;
  2910.      char *promo_char;
  2911. {       
  2912.     *move_type = yylexstr(move_num, machine_move, NULL);
  2913.     switch (*move_type) {
  2914.       case WhitePromotionQueen:
  2915.       case BlackPromotionQueen:
  2916.       case WhitePromotionRook:
  2917.       case BlackPromotionRook:
  2918.       case WhitePromotionBishop:
  2919.       case BlackPromotionBishop:
  2920.       case WhitePromotionKnight:
  2921.       case BlackPromotionKnight:
  2922.       case NormalMove:
  2923.       case WhiteCapturesEnPassant:
  2924.       case BlackCapturesEnPassant:
  2925.       case WhiteKingSideCastle:
  2926.       case WhiteQueenSideCastle:
  2927.       case BlackKingSideCastle:
  2928.       case BlackQueenSideCastle:
  2929.       case WhiteKingSideCastleWild:
  2930.       case WhiteQueenSideCastleWild:
  2931.       case BlackKingSideCastleWild:
  2932.       case BlackQueenSideCastleWild:
  2933.     *from_x = currentMoveString[0] - 'a';
  2934.     *from_y = currentMoveString[1] - '1';
  2935.     *to_x = currentMoveString[2] - 'a';
  2936.     *to_y = currentMoveString[3] - '1';
  2937.     *promo_char = currentMoveString[4];
  2938.     break;
  2939.       case BadMove:
  2940.       case AmbiguousMove:
  2941.       case 0:            /* end of file */
  2942.       case ElapsedTime:
  2943.       case Comment:
  2944.       case StartGame:
  2945.       case WhiteWins:
  2946.       case BlackWins:
  2947.       case GameIsDrawn:
  2948.       default:
  2949.     /* bug? */
  2950.     fprintf(stderr, "%s: bad move in chess program output: %s\n",
  2951.         programName, machine_move);
  2952.     *from_x = *from_y = *to_x = *to_y = 0;
  2953.     break;
  2954.     }
  2955. }
  2956.  
  2957. void HandleMachineMove(message, fp)
  2958.      char *message;
  2959.      FILE *fp;
  2960. {
  2961.     char machine_move[MSG_SIZ], buf1[MSG_SIZ], buf2[MSG_SIZ];
  2962.     int from_x, from_y, to_x, to_y;
  2963.     ChessMove move_type;
  2964.     char promo_char;
  2965.     
  2966.     maybeThinking = False;
  2967.     
  2968.     if (strncmp(message, "warning:", 8) == 0) {
  2969.     DisplayMessage(message);
  2970.     return;
  2971.     }
  2972.     
  2973.     /*
  2974.      * If chess program startup fails, exit with an error message.
  2975.      * Attempts to recover here are futile.
  2976.      */
  2977.     if ((StrStr(message, "unknown host") != NULL)
  2978.     || (StrStr(message, "No remote directory") != NULL)
  2979.     || (StrStr(message, "not found") != NULL)
  2980.     || (StrStr(message, "No such file") != NULL)
  2981.     || (StrStr(message, "Permission denied") != NULL)) {
  2982.     fprintf(stderr,
  2983.         "%s: failed to start chess program %s on %s: %s\n",
  2984.         programName,
  2985.         fp == fromFirstProgFP ? appData.firstChessProgram
  2986.         : appData.secondChessProgram,
  2987.         fp == fromFirstProgFP ? appData.firstHost
  2988.         : appData.secondHost,
  2989.         message);
  2990.     ShutdownChessPrograms(message);
  2991.     exit(1);
  2992.     }
  2993.     
  2994.     if (strncmp(message, "time", 4) == 0) {
  2995.     if (StrStr(message, "CHESS")) {
  2996.         /* Program has a broken "time" command that
  2997.            outputs a string not ending in newline.
  2998.            Don't use it. */
  2999.         if (fp == fromFirstProgFP) firstSendTime = 0;
  3000.         if (fp == fromSecondProgFP) secondSendTime = 0;
  3001.     }
  3002.     }
  3003.     
  3004.     /*
  3005.      * If the move is illegal, cancel it and redraw the board.
  3006.      */
  3007.     if (strncmp(message, "Illegal move", 12) == 0) {
  3008.     
  3009.     if (fp == fromFirstProgFP && firstSendTime == 2) {
  3010.         /* First program doesn't have the "time" command */
  3011.         firstSendTime = 0;
  3012.         return;
  3013.     } else if (fp == fromSecondProgFP && secondSendTime == 2) {
  3014.         /* Second program doesn't have the "time" command */
  3015.         secondSendTime = 0;
  3016.         return;
  3017.     }
  3018.     if (forwardMostMove <= backwardMostMove) return;
  3019.     if (gameMode == PauseGame) PauseProc(NULL, NULL, NULL, NULL);
  3020.     if (gameMode == PlayFromGameFile) {
  3021.         /* Stop reading this game file */
  3022.         gameMode = ForceMoves;
  3023.         ModeHighlight();
  3024.     }
  3025.     currentMove = --forwardMostMove;
  3026.     if ((gameMode == PlayFromGameFile) || (gameMode == ForceMoves))
  3027.       DisplayClocks(ReDisplayTimers);
  3028.     else
  3029.       DisplayClocks(SwitchTimers);
  3030.     sprintf(buf1, "Illegal move: %s", parseList[currentMove]);
  3031.     DisplayMessage(buf1);
  3032.     
  3033.     DrawPosition(boardWidget, NULL, NULL, NULL);
  3034.     
  3035.     XSync(xDisplay, False);
  3036.     return;
  3037.     }
  3038.     
  3039.     if (strncmp(message, "Hint:", 5) == 0) {
  3040.     sscanf(message, "Hint: %s", machine_move);
  3041.     ParseMachineMove(machine_move, forwardMostMove, &move_type,
  3042.              &from_x, &from_y, &to_x, &to_y, &promo_char);
  3043.     move_type = CoordsToAlgebraic(from_x, from_y, to_x, to_y, promo_char,
  3044.                       forwardMostMove, buf1);
  3045.     sprintf(buf2, "Hint: %s", buf1);
  3046.     DisplayMessage(buf2);
  3047.     return;
  3048.     }
  3049.     
  3050.     /*
  3051.      * win, lose or draw
  3052.      */
  3053.     if (strncmp(message, "White", 5) == 0) {
  3054.     ShutdownChessPrograms("White mates");
  3055.     return;
  3056.     } else if (strncmp(message, "Black", 5) == 0) {
  3057.     ShutdownChessPrograms("Black mates");
  3058.     return;
  3059.     } else if (strncmp(message, "opponent mates!", 15) == 0) {
  3060.     switch (gameMode == PauseGame ? pausePreviousMode : gameMode) {
  3061.       case MachinePlaysBlack:
  3062.         ShutdownChessPrograms("White mates");
  3063.         break;
  3064.       case MachinePlaysWhite:
  3065.         ShutdownChessPrograms("Black mates");
  3066.         break;
  3067.       case TwoMachinesPlay:
  3068.         ShutdownChessPrograms(fp == fromFirstProgFP ?
  3069.                   "White mates" : "Black mates");
  3070.         break;
  3071.       default:
  3072.         /* can't happen */
  3073.         break;
  3074.     }
  3075.     return;
  3076.     } else if (strncmp(message, "computer mates!", 15) == 0) {
  3077.     switch (gameMode == PauseGame ? pausePreviousMode : gameMode) {
  3078.       case MachinePlaysBlack:
  3079.         ShutdownChessPrograms("Black mates");
  3080.         break;
  3081.       case MachinePlaysWhite:
  3082.         ShutdownChessPrograms("White mates");
  3083.         break;
  3084.       case TwoMachinesPlay:
  3085.         ShutdownChessPrograms(fp == fromFirstProgFP ?
  3086.                   "Black mates" : "White mates");
  3087.         break;
  3088.       default:
  3089.         /* can't happen */
  3090.         break;
  3091.     }
  3092.     return;
  3093.     } else if (strncmp(message, "Draw", 4) == 0) {
  3094.     ShutdownChessPrograms("Draw");
  3095.     return;
  3096.     }
  3097.     
  3098.     /*
  3099.      * normal machine reply move
  3100.      */
  3101.     maybeThinking = True;
  3102.     if (StrStr(message, "...") != NULL) {
  3103.     sscanf(message, "%s %s %s", buf1, buf2, machine_move);
  3104.     if (machine_move[0] == NULLCHAR)
  3105.       return;
  3106.     } else
  3107.       return;        /* ignore noise */
  3108.     
  3109.     strcpy(moveList[forwardMostMove], machine_move);
  3110.     
  3111.     ParseMachineMove(machine_move, forwardMostMove, &move_type,
  3112.              &from_x, &from_y, &to_x, &to_y, &promo_char);
  3113.     
  3114.     if (gameMode != PauseGame)
  3115.       currentMove = forwardMostMove;  /*display latest move*/
  3116.     
  3117.     MakeMove(&move_type, from_x, from_y, to_x, to_y);
  3118.     
  3119.     if (gameMode != PauseGame && appData.ringBellAfterMoves)
  3120.       putc(BELLCHAR, stderr);
  3121.     
  3122.     if (gameMode == TwoMachinesPlay ||
  3123.     (gameMode == PauseGame && pausePreviousMode == TwoMachinesPlay)) {
  3124.     strcat(machine_move, "\n");
  3125.     if (WhiteOnMove(forwardMostMove)) {
  3126.         Attention(secondProgramPID);
  3127.         if (secondSendTime) 
  3128.           SendTimeRemaining(toSecondProgFP);
  3129.         SendToProgram(machine_move, toSecondProgFP);
  3130.         if (firstMove) {
  3131.         firstMove = False;
  3132.         SendToProgram(appData.whiteString,
  3133.                   toSecondProgFP);
  3134.         }
  3135.     } else {
  3136.         Attention(firstProgramPID);
  3137.         if (firstSendTime)
  3138.           SendTimeRemaining(toFirstProgFP);
  3139.         SendToProgram(machine_move, toFirstProgFP);
  3140.         if (firstMove) {
  3141.         firstMove = False;
  3142.         SendToProgram(appData.blackString,
  3143.                   toFirstProgFP);
  3144.         }
  3145.     }
  3146.     }
  3147. }
  3148.  
  3149.  
  3150. /* Parse a game score from the character string "game", and
  3151.    record it as the history of the current game.  The game
  3152.    score is NOT assumed to start from the standard position. 
  3153.    The display is not updated in any way.
  3154.    */
  3155. void ParseGameHistory(game)
  3156.      char *game;
  3157. {
  3158.     ChessMove move_type;
  3159.     int from_x, from_y, to_x, to_y, boardIndex;
  3160.     char promo_char;
  3161.     char *p;
  3162.  
  3163.     if (appData.debugMode)
  3164.       fprintf(stderr, "Parsing game history: %s\n", game);
  3165.  
  3166.     /* Parse out names of players */
  3167.     while (*game == ' ') game++;
  3168.     p = ics_white;
  3169.     while (*game != ' ') *p++ = *game++;
  3170.     *p = NULLCHAR;
  3171.     while (*game == ' ') game++;
  3172.     p = ics_black;
  3173.     while (*game != ' ' && *game != '\n') *p++ = *game++;
  3174.     *p = NULLCHAR;
  3175.  
  3176.     /* Parse moves */
  3177.     boardIndex = 0;
  3178.     for (;;) {
  3179.     move_type = yylexstr(boardIndex, game, &game);
  3180.     switch (move_type) {
  3181.       case WhitePromotionQueen:
  3182.       case BlackPromotionQueen:
  3183.       case WhitePromotionRook:
  3184.       case BlackPromotionRook:
  3185.       case WhitePromotionBishop:
  3186.       case BlackPromotionBishop:
  3187.       case WhitePromotionKnight:
  3188.       case BlackPromotionKnight:
  3189.       case NormalMove:
  3190.       case WhiteCapturesEnPassant:
  3191.       case BlackCapturesEnPassant:
  3192.       case WhiteKingSideCastle:
  3193.       case WhiteQueenSideCastle:
  3194.       case BlackKingSideCastle:
  3195.       case BlackQueenSideCastle:
  3196.       case WhiteKingSideCastleWild:
  3197.       case WhiteQueenSideCastleWild:
  3198.       case BlackKingSideCastleWild:
  3199.       case BlackQueenSideCastleWild:
  3200.         from_x = currentMoveString[0] - 'a';
  3201.         from_y = currentMoveString[1] - '1';
  3202.         to_x = currentMoveString[2] - 'a';
  3203.         to_y = currentMoveString[3] - '1';
  3204.         promo_char = currentMoveString[4];
  3205.         break;
  3206.       case BadMove:
  3207.       case AmbiguousMove:
  3208.         /* bug? */
  3209.         fprintf(stderr, "%s: ?bad move in ICS output: %s\n",
  3210.             programName, yytext);
  3211.         /* fall thru */
  3212.       case 0:    /* end of file */
  3213.         if (boardIndex < backwardMostMove) {
  3214.         /* Oops, gap.  How did that happen? */
  3215.         return;
  3216.         }
  3217.         backwardMostMove = 0;
  3218.         if (boardIndex > forwardMostMove) {
  3219.         forwardMostMove = boardIndex;
  3220.         }
  3221.         return;
  3222.       case ElapsedTime:
  3223.         if (boardIndex > 0) {
  3224.         strcat(parseList[boardIndex-1], " ");
  3225.         strcat(parseList[boardIndex-1], yytext);
  3226.         }
  3227.         continue;
  3228.       case Comment:
  3229.       case StartGame:
  3230.       default:
  3231.         /* ignore */
  3232.         continue;
  3233.       case WhiteWins:
  3234.       case BlackWins:
  3235.       case GameIsDrawn:
  3236.         strncpy(endMessage, yytext, MOVE_LEN * 4);
  3237.         endMessage[MOVE_LEN * 4 - 1] = NULLCHAR;
  3238.         continue;
  3239.     }
  3240.     (void) CoordsToAlgebraic(from_x, from_y, to_x, to_y, promo_char,
  3241.                  boardIndex, parseList[boardIndex]);
  3242.     CopyBoard(boards[boardIndex + 1], boards[boardIndex]);
  3243.     boardIndex++;
  3244.     ApplyMove(&move_type, from_x, from_y, to_x, to_y,
  3245.           boards[boardIndex]);
  3246.     }
  3247. }
  3248.  
  3249. void LoadGameLoop()
  3250. {
  3251.     readGameXID = 0;
  3252.     for (;;) {
  3253.     if (!LoadGameOneMove())
  3254.       return;
  3255.     if (matchMode == MatchOpening || appData.timeDelay == 0)
  3256.       continue;
  3257.     if (appData.timeDelay < 0)
  3258.       return;
  3259.     readGameXID =
  3260.       XtAppAddTimeOut(appContext, (int) (1000 * appData.timeDelay),
  3261.               (XtTimerCallbackProc) LoadGameLoop, NULL);
  3262.     break;
  3263.     }
  3264. }
  3265.  
  3266. Boolean LoadGameOneMove()
  3267. {
  3268.     int from_x, from_y, to_x, to_y, done;
  3269.     ChessMove move_type;
  3270.     char move[MSG_SIZ];
  3271.     
  3272.     if (gameFileFP == NULL)
  3273.       return False;
  3274.     
  3275.     if (!(gameMode == PlayFromGameFile ||
  3276.       (gameMode == PauseGame && pausePreviousMode == PlayFromGameFile))) {
  3277.     fclose(gameFileFP);
  3278.     gameFileFP = NULL;
  3279.     yynewfile();
  3280.     return False;
  3281.     }
  3282.     
  3283.     if (commentUp) CommentPopDown();
  3284.     
  3285.     yyboardindex = forwardMostMove;
  3286.     move_type = (ChessMove) yylex();
  3287.     
  3288.     if (appData.debugMode) {
  3289.     switch (move_type) {
  3290.       case BadMove:
  3291.         fprintf(stderr, "Parsed BadMove: %s\n", yytext);
  3292.         break;
  3293.       case AmbiguousMove:
  3294.         fprintf(stderr, "Parsed AmbiguousMove: %s\n", yytext);
  3295.         break;
  3296.       case Comment:
  3297.         fprintf(stderr, "Parsed Comment: %s\n", yytext);
  3298.         break;
  3299.       case WhiteWins:
  3300.       case BlackWins:
  3301.       case GameIsDrawn:
  3302.       case 0:
  3303.         fprintf(stderr, "Parsed game end: %s\n", yytext);
  3304.         break;
  3305.       case StartGame:
  3306.         fprintf(stderr, "Parsed StartGame: %s\n", yytext);
  3307.         break;
  3308.       case ElapsedTime:
  3309.         fprintf(stderr, "Parsed ElapsedTime: %s\n", yytext);
  3310.         break;
  3311.       default:
  3312.         fprintf(stderr, "Parsed %s into %s", yytext, currentMoveString);
  3313.         break;
  3314.     }
  3315.     }
  3316.     
  3317.     done = False;
  3318.     switch (move_type) {
  3319.       case Comment:
  3320.     CommentPopUp(yytext);
  3321.     return True;
  3322.       case WhiteCapturesEnPassant:
  3323.       case BlackCapturesEnPassant:
  3324.       case WhitePromotionQueen:
  3325.       case BlackPromotionQueen:
  3326.       case WhitePromotionRook:
  3327.       case BlackPromotionRook:
  3328.       case WhitePromotionBishop:
  3329.       case BlackPromotionBishop:
  3330.       case WhitePromotionKnight:
  3331.       case BlackPromotionKnight:
  3332.       case NormalMove:
  3333.       case WhiteKingSideCastle:
  3334.       case WhiteQueenSideCastle:
  3335.       case BlackKingSideCastle:
  3336.       case BlackQueenSideCastle:
  3337.       case WhiteKingSideCastleWild:
  3338.       case WhiteQueenSideCastleWild:
  3339.       case BlackKingSideCastleWild:
  3340.       case BlackQueenSideCastleWild:
  3341.     from_x = currentMoveString[0] - 'a';
  3342.     from_y = currentMoveString[1] - '1';
  3343.     to_x = currentMoveString[2] - 'a';
  3344.     to_y = currentMoveString[3] - '1';
  3345.     break;
  3346.       case 0:  /* end of file */
  3347.     DisplayMessage("End of game file");
  3348.     done = True;
  3349.     break;
  3350.       case WhiteWins:
  3351.       case BlackWins:
  3352.       case GameIsDrawn:
  3353.     GameEnds(yytext);
  3354.     return False;
  3355.       case StartGame:
  3356. #ifdef notdef
  3357.     /* We see too many bogus StartGame markers with current
  3358.        lex definition, especially in gnuchess listing files,
  3359.        so this code is disabled. */
  3360.     /* Reached start of next game in file */
  3361.     DisplayMessage("End of game");
  3362.     done = True;
  3363. #else /*!notdef*/
  3364.     /* ignore */
  3365.     return LoadGameOneMove(); /* tail recursion */
  3366. #endif /*notdef*/
  3367.     break;
  3368.       case ElapsedTime:
  3369.     /* ignore */
  3370.     return LoadGameOneMove(); /* tail recursion */
  3371.       default:
  3372.       case BadMove:
  3373.     sprintf(move, "Bad move: %d. %s%s",
  3374.         (forwardMostMove / 2) + 1,
  3375.         WhiteOnMove(forwardMostMove) ? "" : "... ", yytext);
  3376.     DisplayMessage(move);
  3377.     done = True;
  3378.     break;
  3379.       case AmbiguousMove:
  3380.     sprintf(move, "Ambiguous move: %d. %s%s",
  3381.         (forwardMostMove / 2) + 1,
  3382.         WhiteOnMove(forwardMostMove) ? "" : "... ", yytext);
  3383.     DisplayMessage(move);
  3384.     done = True;
  3385.     break;
  3386.     }
  3387.     
  3388.     if (done) {
  3389.     lastGameMode = (gameMode == PauseGame ? pausePreviousMode : gameMode);
  3390.     gameMode = ForceMoves;
  3391.     ModeHighlight();
  3392.     if (readGameXID != 0) {
  3393.         XtRemoveTimeOut(readGameXID);
  3394.         readGameXID = 0;
  3395.     }
  3396.     fclose(gameFileFP);
  3397.     gameFileFP = NULL;
  3398.     yynewfile();
  3399.     return (int) False;
  3400.     } else {
  3401.     SendToProgram(currentMoveString, toFirstProgFP);
  3402.     strcpy(moveList[forwardMostMove], currentMoveString);
  3403.     
  3404.     MakeMove(&move_type, from_x, from_y, to_x, to_y);
  3405.     
  3406.     return (int) True;
  3407.     }
  3408. }
  3409.  
  3410. /* Apply a move to the given board.  Oddity: move_type is ignored on
  3411.    input unless the move is seen to be a pawn promotion, in which case
  3412.    move_type tells us what to promote to.
  3413. */
  3414. void ApplyMove(move_type, from_x, from_y, to_x, to_y, board)
  3415.      ChessMove *move_type;
  3416.      int from_x, from_y, to_x, to_y;
  3417.      Board board;
  3418. {
  3419.     if (from_y == 0 && from_x == 4
  3420.     && board[from_y][from_x] == WhiteKing
  3421.     && to_y == 0 && to_x == 6) {
  3422.     *move_type = WhiteKingSideCastle;
  3423.     board[from_y][from_x] = EmptySquare;
  3424.     board[to_y][to_x] = WhiteKing;
  3425.     board[from_y][7] = EmptySquare;
  3426.     board[to_y][5] = WhiteRook;
  3427.     } else if (from_y == 0 && from_x == 4
  3428.            && board[from_y][from_x] == WhiteKing
  3429.            && to_y == 0 && to_x == 2) {
  3430.     *move_type = WhiteQueenSideCastle;
  3431.     board[from_y][from_x] = EmptySquare;
  3432.     board[to_y][to_x] = WhiteKing;
  3433.     board[from_y][0] = EmptySquare;
  3434.     board[to_y][3] = WhiteRook;
  3435.     } else if (from_y == 0 && from_x == 3
  3436.     && board[from_y][from_x] == WhiteKing
  3437.     && to_y == 0 && to_x == 5) {
  3438.     *move_type = WhiteKingSideCastleWild;
  3439.     board[from_y][from_x] = EmptySquare;
  3440.     board[to_y][to_x] = WhiteKing;
  3441.     board[from_y][7] = EmptySquare;
  3442.     board[to_y][4] = WhiteRook;
  3443.     } else if (from_y == 0 && from_x == 3
  3444.            && board[from_y][from_x] == WhiteKing
  3445.            && to_y == 0 && to_x == 1) {
  3446.     *move_type = WhiteQueenSideCastleWild;
  3447.     board[from_y][from_x] = EmptySquare;
  3448.     board[to_y][to_x] = WhiteKing;
  3449.     board[from_y][0] = EmptySquare;
  3450.     board[to_y][2] = WhiteRook;
  3451.     } else if (from_y == 6
  3452.            && board[from_y][from_x] == WhitePawn
  3453.            && to_y == 7) {
  3454.     /* white pawn promotion */
  3455.     board[7][to_x] = PromoPiece(*move_type);
  3456.     if (board[7][to_x] == EmptySquare) {
  3457.         board[7][to_x] = WhiteQueen;
  3458.         *move_type = WhitePromotionQueen;
  3459.     }
  3460.     board[6][from_x] = EmptySquare;
  3461.     } else if ((from_y == 4)
  3462.            && (to_x != from_x)
  3463.            && (board[from_y][from_x] == WhitePawn)
  3464.            && (board[to_y][to_x] == EmptySquare)) {
  3465.     *move_type = WhiteCapturesEnPassant;
  3466.     board[from_y][from_x] = EmptySquare;
  3467.     board[to_y][to_x] = WhitePawn;
  3468.     board[to_y - 1][to_x] = EmptySquare;
  3469.     } else if (from_y == 7 && from_x == 4
  3470.            && board[from_y][from_x] == BlackKing
  3471.            && to_y == 7 && to_x == 6) {
  3472.     *move_type = BlackKingSideCastle;
  3473.     board[from_y][from_x] = EmptySquare;
  3474.     board[to_y][to_x] = BlackKing;
  3475.     board[from_y][7] = EmptySquare;
  3476.     board[to_y][5] = BlackRook;
  3477.     } else if (from_y == 7 && from_x == 4
  3478.            && board[from_y][from_x] == BlackKing
  3479.            && to_y == 7 && to_x == 2) {
  3480.     *move_type = BlackQueenSideCastle;
  3481.     board[from_y][from_x] = EmptySquare;
  3482.     board[to_y][to_x] = BlackKing;
  3483.     board[from_y][0] = EmptySquare;
  3484.     board[to_y][3] = BlackRook;
  3485.     } else if (from_y == 7 && from_x == 3
  3486.            && board[from_y][from_x] == BlackKing
  3487.            && to_y == 7 && to_x == 5) {
  3488.     *move_type = BlackKingSideCastleWild;
  3489.     board[from_y][from_x] = EmptySquare;
  3490.     board[to_y][to_x] = BlackKing;
  3491.     board[from_y][7] = EmptySquare;
  3492.     board[to_y][4] = BlackRook;
  3493.     } else if (from_y == 7 && from_x == 3
  3494.            && board[from_y][from_x] == BlackKing
  3495.            && to_y == 7 && to_x == 1) {
  3496.     *move_type = BlackQueenSideCastleWild;
  3497.     board[from_y][from_x] = EmptySquare;
  3498.     board[to_y][to_x] = BlackKing;
  3499.     board[from_y][0] = EmptySquare;
  3500.     board[to_y][2] = BlackRook;
  3501.     } else if (from_y == 1
  3502.            && board[from_y][from_x] == BlackPawn
  3503.            && to_y == 0) {
  3504.     /* black pawn promotion */
  3505.     board[0][to_x] = PromoPiece(*move_type);
  3506.     if (board[0][to_x] == EmptySquare) {
  3507.         board[0][to_x] = BlackQueen;
  3508.         *move_type = BlackPromotionQueen;
  3509.     }
  3510.     board[1][from_x] = EmptySquare;
  3511.     } else if ((from_y == 3)
  3512.            && (to_x != from_x)
  3513.            && (board[from_y][from_x] == BlackPawn)
  3514.            && (board[to_y][to_x] == EmptySquare)) {
  3515.     *move_type = BlackCapturesEnPassant;
  3516.     board[from_y][from_x] = EmptySquare;
  3517.     board[to_y][to_x] = BlackPawn;
  3518.     board[to_y + 1][to_x] = EmptySquare;
  3519.     } else {
  3520.     *move_type = NormalMove;
  3521.     board[to_y][to_x] = board[from_y][from_x];
  3522.     board[from_y][from_x] = EmptySquare;
  3523.     }
  3524. }
  3525.  
  3526. /*
  3527.  * MakeMove() displays moves.  If they are illegal, GNU chess will detect
  3528.  * this and send an Illegal move message.  XBoard will then retract the move.
  3529.  *
  3530.  * Oddity: move_type is ignored on input unless the move is seen to be a
  3531.  * pawn promotion, in which case move_type tells us what to promote to.
  3532.  */
  3533. void MakeMove(move_type, from_x, from_y, to_x, to_y)
  3534.      ChessMove *move_type;
  3535.      int from_x, from_y, to_x, to_y;
  3536. {
  3537.     forwardMostMove++;
  3538.     CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);
  3539.     ApplyMove(move_type, from_x, from_y, to_x, to_y,
  3540.           boards[forwardMostMove]);
  3541.     endMessage[0] = NULLCHAR;
  3542.     (void) CoordsToAlgebraic(from_x, from_y, to_x, to_y,
  3543.                  ToLower(PieceToChar(PromoPiece(*move_type))),
  3544.                  forwardMostMove - 1,
  3545.                  parseList[forwardMostMove - 1]);
  3546.     timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
  3547.     timeRemaining[1][forwardMostMove] = blackTimeRemaining;
  3548.  
  3549.     if (gameMode == PauseGame && pausePreviousMode != PlayFromGameFile)
  3550.       return;
  3551.     
  3552.     currentMove = forwardMostMove;
  3553.     if ((gameMode == PlayFromGameFile) || (gameMode == ForceMoves) ||
  3554.     (gameMode == PauseGame && pausePreviousMode == PlayFromGameFile))
  3555.       DisplayClocks(ReDisplayTimers);
  3556.     else
  3557.       DisplayClocks(SwitchTimers);
  3558.     DisplayMove(currentMove - 1);
  3559.     DrawPosition(boardWidget, NULL, NULL, NULL);
  3560.     XSync(xDisplay, False);
  3561. }
  3562.  
  3563. void InitChessProgram(host_name, program_name, pid, to, from, xid, sendTime)
  3564.      char *host_name, *program_name;
  3565.      int *pid;
  3566.      FILE **to, **from;
  3567.      XtIntervalId *xid;
  3568.      int *sendTime;
  3569. {
  3570.     char arg_buf[10];
  3571.     char *arg1, *arg2;
  3572.     int to_prog[2], from_prog[2];
  3573.     FILE *from_fp, *to_fp;
  3574.     int dummy_source;
  3575.     XtInputId dummy_id;
  3576. #if defined(SYSTEM_FIVE) || defined(SYSV)
  3577.     char *pty_name;
  3578. #endif
  3579.     
  3580.     if (appData.noChessProgram) return;
  3581.     
  3582. #if defined(SYSTEM_FIVE) || defined(SYSV)
  3583.     if ((pty_name = PseudoTTY(&to_prog[1])) == NULL) {
  3584.     fprintf(stderr, "%s: can't open pseudo-tty: ", programName);
  3585.     perror("");
  3586.     exit(1);
  3587.     }
  3588.     
  3589.     from_prog[0] = to_prog[1];
  3590.     to_prog[0] = from_prog[1] = open(pty_name, O_RDWR, 0);
  3591. #ifdef SVR4
  3592.     if (ioctl (to_prog[0], I_PUSH, "ptem") == -1 ||
  3593.     ioctl (to_prog[0], I_PUSH, "ldterm") == -1 ||
  3594.     ioctl (to_prog[0], I_PUSH, "ttcompat") == -1) {
  3595.     fprintf(stderr, "%s: can't ioctl pseudo-tty: ", programName);
  3596.     perror("");
  3597.     exit(1);
  3598.     }
  3599. #endif
  3600. #else
  3601.     signal(SIGPIPE, CatchPipeSignal);
  3602.     pipe(to_prog);
  3603.     pipe(from_prog);
  3604. #endif
  3605.     
  3606.     if ((*pid = fork()) == 0) {
  3607. #if !defined(SYSTEM_FIVE) && !defined(SYSV)
  3608.     signal(SIGPIPE, CatchPipeSignal);
  3609. #endif
  3610.     dup2(to_prog[0], 0);
  3611.     dup2(from_prog[1], 1);
  3612.     close(to_prog[0]);
  3613.     close(to_prog[1]);
  3614.     close(from_prog[0]);
  3615.     close(from_prog[1]);
  3616.     dup2(1, fileno(stderr)); /* force stderr to the pipe */
  3617.     if (*appData.searchTime != NULLCHAR) {
  3618.         sprintf(arg_buf, "%d", searchTime);
  3619.         arg1 = arg_buf;
  3620.         arg2 = (char *) NULL;
  3621.     } else if (appData.searchDepth > 0) {
  3622.         sprintf(arg_buf, "%d", appData.searchDepth);
  3623.         arg1 = "1";
  3624.         arg2 = "9999";
  3625.     } else {
  3626.         sprintf(arg_buf, "%d", appData.movesPerSession);
  3627.         arg1 = arg_buf;
  3628.         arg2 = appData.timeControl;
  3629.     }
  3630.     if (strcmp(host_name, "localhost") == 0) {
  3631.         execlp(program_name, program_name, arg1, arg2,
  3632.            (char *) NULL);
  3633.     } else {
  3634.         execlp(appData.remoteShell, appData.remoteShell,
  3635.            host_name, program_name, arg1, arg2,
  3636.            (char *) NULL);
  3637.     }
  3638.     
  3639.     perror(program_name);
  3640.     exit(1);
  3641.     }
  3642.     
  3643.     close(to_prog[0]);
  3644.     close(from_prog[1]);
  3645.     
  3646.     *from = from_fp = fdopen(from_prog[0], "r");
  3647.     *to = to_fp = fdopen(to_prog[1], "w");
  3648.     setbuf(from_fp, NULL); setbuf(to_fp, NULL);
  3649.     
  3650.     ReceiveFromProgram(from_fp, &dummy_source, &dummy_id); /*"Chess"*/
  3651.     if (*pid == 0) return;
  3652.     
  3653.     *xid = XtAppAddInput(appContext, fileno(from_fp), 
  3654.              (XtPointer)XtInputReadMask,
  3655.              (XtInputCallbackProc)ReceiveFromProgram, 
  3656.              (XtPointer)from_fp);
  3657.     
  3658.     SendToProgram(appData.initString, to_fp);
  3659.     SendSearchDepth(to_fp);
  3660.     
  3661.     if (*sendTime == 2) {
  3662.     /* Does program have "time" command? */
  3663.     char buf[MSG_SIZ];
  3664.     
  3665.     sprintf(buf, "time %d\nhelp\n", blackTimeRemaining/10);
  3666.     /* "help" is a kludge to work around a gnuchess bug;
  3667.        some versions do not send a newline at the end of
  3668.        their response to the time command */
  3669.     SendToProgram(buf, to_fp);
  3670.     ReceiveFromProgram(from_fp, &dummy_source, &dummy_id);
  3671.     if (*sendTime == 2) *sendTime = 1;  /* yes! */
  3672.     }
  3673.     
  3674. }
  3675.  
  3676. void GameEnds(why)
  3677.      char *why;
  3678. {
  3679.     ics_mode = IcsIdle;
  3680.     ics_gamenum = -1;
  3681.     ics_user_moved = False;
  3682.     if (readGameXID != 0) {
  3683.     XtRemoveTimeOut(readGameXID);
  3684.     readGameXID = 0;
  3685.     }
  3686.     if (gameFileFP != NULL) {
  3687.     fclose(gameFileFP);
  3688.     gameFileFP = NULL;
  3689.     yynewfile();
  3690.     }
  3691.     DisplayClocks(StopTimers);
  3692.     
  3693.     if (why == NULL) return;
  3694.     
  3695.     strncpy(endMessage, why, MOVE_LEN * 4);
  3696.     endMessage[MOVE_LEN * 4 - 1] = NULLCHAR;
  3697.     if (currentMove == forwardMostMove)
  3698.       DisplayMove(currentMove - 1);
  3699.     
  3700.     if (!(gameMode == PlayFromGameFile ||
  3701.       (gameMode == PauseGame && pausePreviousMode == PlayFromGameFile))) {
  3702.     if (*appData.saveGameFile != NULLCHAR) {
  3703.         SaveGame(appData.saveGameFile);
  3704.     } else if (appData.autoSaveGames) {
  3705.         SaveGameProc(NULL, NULL, NULL, NULL);
  3706.     }
  3707.     if (appData.savePositionFile[0] != NULLCHAR) 
  3708.       SavePosition(appData.savePositionFile);
  3709.     }
  3710. }
  3711.  
  3712. void ShutdownChessPrograms(why)
  3713.      char *why;
  3714. {
  3715.     GameEnds(why);
  3716.     
  3717.     lastGameMode = gameMode;
  3718.     if (gameMode == PauseGame) {
  3719.     pausePreviousMode = EndOfGame;
  3720.     } else {
  3721.     gameMode = EndOfGame;
  3722.     ModeHighlight();
  3723.     }
  3724.     
  3725.     if (firstProgramPID != 0) {
  3726.     if (kill(firstProgramPID, 0) == 0)
  3727.       SendToProgram("quit\n", toFirstProgFP);
  3728.     fclose(fromFirstProgFP);
  3729.     fclose(toFirstProgFP);
  3730.     fromFirstProgFP = toFirstProgFP = NULL;
  3731.     if (kill(firstProgramPID, SIGTERM)==0) 
  3732.       wait((union wait *)0);
  3733.     
  3734.     }
  3735.     firstProgramPID = 0;
  3736.     
  3737.     if (firstProgramXID != 0)
  3738.       XtRemoveInput(firstProgramXID);
  3739.     firstProgramXID = 0;
  3740.     
  3741.     if (secondProgramPID != 0) {
  3742.     if (kill(secondProgramPID, 0) == 0)
  3743.       SendToProgram("quit\n", toSecondProgFP);
  3744.     fclose(fromSecondProgFP);
  3745.     fclose(toSecondProgFP);
  3746.     fromSecondProgFP = toSecondProgFP = NULL;
  3747.     if (kill(secondProgramPID, SIGTERM)==0) 
  3748.       wait((union wait *)0);
  3749.     }
  3750.     secondProgramPID = 0;
  3751.     
  3752.     if (secondProgramXID != 0)
  3753.       XtRemoveInput(secondProgramXID);
  3754.     secondProgramXID = 0;
  3755.     
  3756.     if (matchMode != MatchFalse) exit(0);
  3757. }
  3758.  
  3759. void CommentPopDown()
  3760. {
  3761.     Arg args[2];
  3762.     
  3763.     XtSetArg(args[0], XtNx, &commentX);
  3764.     XtSetArg(args[1], XtNy, &commentY);
  3765.     XtGetValues(commentShell, args, 2);
  3766.     XtPopdown(commentShell);
  3767.     XtDestroyWidget(commentShell);
  3768.     commentUp = False;
  3769. }
  3770.  
  3771.  
  3772. void CommentPopUp(label)
  3773.      char *label;
  3774. {
  3775.     Arg args[2];
  3776.     Position x, y;
  3777.     Dimension bw_width, pw_width;
  3778.     
  3779.     if (commentUp) CommentPopDown();
  3780.     
  3781.     XtSetArg(args[0], XtNwidth, &bw_width);
  3782.     XtGetValues(formWidget, args, 1);
  3783.     
  3784.     XtSetArg(args[0], XtNresizable, True);
  3785.     XtSetArg(args[1], XtNwidth, bw_width - 8);
  3786.     
  3787.     commentShell =
  3788.       XtCreatePopupShell("Comment", transientShellWidgetClass,
  3789.              commandsWidget, args, 2);
  3790.     
  3791.     XtSetArg(args[0], XtNlabel, label);
  3792.     
  3793.     (void) XtCreateManagedWidget("commentLabel", labelWidgetClass,
  3794.                  commentShell, args, 1);
  3795.     
  3796.     XtRealizeWidget(commentShell);
  3797.     
  3798.     XtSetArg(args[0], XtNwidth, &pw_width);
  3799.     XtGetValues(commentShell, args, 1);
  3800.     
  3801.     if (commentX == -1) {
  3802.     XtTranslateCoords(shellWidget, (bw_width - pw_width) / 2, -50, &x, &y);
  3803.     } else {
  3804.     x = commentX - appData.borderXoffset;
  3805.     y = commentY - appData.borderYoffset;
  3806.     }
  3807.  
  3808.     XtSetArg(args[0], XtNx, x);
  3809.     XtSetArg(args[1], XtNy, y);
  3810.     XtSetValues(commentShell, args, 2);
  3811.     
  3812.     XtPopup(commentShell, XtGrabNone);
  3813.  
  3814.     commentUp = True;
  3815. }
  3816.  
  3817. void FileNamePopUp(label, def, proc)
  3818.      char *label;
  3819.      char *def;
  3820.      Boolean (*proc) P((char *name));
  3821. {
  3822.     Arg args[2];
  3823.     Widget popup, dialog;
  3824.     Dimension bw_width, pw_width;
  3825.     Window root, child;
  3826.     int x, y;
  3827.     int win_x, win_y;
  3828.     unsigned int mask;
  3829.     
  3830.     fileProc = proc;
  3831.     
  3832.     XtSetArg(args[0], XtNwidth, &bw_width);
  3833.     XtGetValues(boardWidget, args, 1);
  3834.     
  3835.     XtSetArg(args[0], XtNresizable, True);
  3836.     XtSetArg(args[1], XtNwidth, DIALOG_SIZE);
  3837.     
  3838.     popup =
  3839.       XtCreatePopupShell("File Name Prompt", transientShellWidgetClass,
  3840.              commandsWidget, args, 2);
  3841.     
  3842.     XtSetArg(args[0], XtNlabel, label);
  3843.     XtSetArg(args[1], XtNvalue, def);
  3844.     
  3845.     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
  3846.                    popup, args, 2);
  3847.     
  3848.     XawDialogAddButton(dialog, "ok", FileNameCallback, (XtPointer) dialog);
  3849.     XawDialogAddButton(dialog, "cancel", FileNameCallback,
  3850.                (XtPointer) dialog);
  3851.     
  3852.     XtRealizeWidget(popup);
  3853.     
  3854.     XtSetArg(args[0], XtNwidth, &pw_width);
  3855.     XtGetValues(popup, args, 1);
  3856.     
  3857.     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
  3858.           &x, &y, &win_x, &win_y, &mask);
  3859.     
  3860.     XtSetArg(args[0], XtNx, x - 10);
  3861.     XtSetArg(args[1], XtNy, y - 10);
  3862.     XtSetValues(popup, args, 2);
  3863.     
  3864.     XtPopup(popup, XtGrabExclusive);
  3865.     filenameUp = True;
  3866.     
  3867.     XtSetKeyboardFocus(shellWidget, popup);
  3868. }
  3869.  
  3870. void FileNameCallback(w, client_data, call_data)
  3871.      Widget w;
  3872.      XtPointer client_data, call_data;
  3873. {
  3874.     String name;
  3875.     Arg args[1];
  3876.     
  3877.     XtSetArg(args[0], XtNlabel, &name);
  3878.     XtGetValues(w, args, 1);
  3879.     
  3880.     if (strcmp(name, "cancel") == 0) {
  3881.     XtPopdown(w = XtParent(XtParent(w)));
  3882.     XtDestroyWidget(w);
  3883.     filenameUp = False;
  3884.     ModeHighlight();
  3885.     return;
  3886.     }
  3887.     
  3888.     FileNameAction(w, NULL, NULL, NULL);
  3889. }
  3890.  
  3891. void FileNameAction(w, event, prms, nprms)
  3892.      Widget w;
  3893.      XEvent *event;
  3894.      String *prms;
  3895.      Cardinal *nprms;
  3896. {
  3897.     char buf[MSG_SIZ];
  3898.     String name;
  3899.  
  3900.     name = XawDialogGetValueString(w = XtParent(w));
  3901.     
  3902.     if ((name != NULL) && (*name != NULLCHAR)) {
  3903.     strcpy(buf, name);
  3904.     XtPopdown(w = XtParent(w));
  3905.     XtDestroyWidget(w);
  3906.     filenameUp = False;
  3907.     (void) (*fileProc)(buf); /* I can't see a way not
  3908.                     to use a global here */
  3909.     ModeHighlight();
  3910.     return;
  3911.     }
  3912.     
  3913.     XtPopdown(w = XtParent(w));
  3914.     XtDestroyWidget(w);
  3915.     filenameUp = False;
  3916.     ModeHighlight();
  3917. }
  3918.  
  3919. typedef struct {
  3920.     ChessSquare piece;
  3921.     int to_x, to_y;
  3922. } PromotionMoveInfo;
  3923.  
  3924. static PromotionMoveInfo pmi;  /*making this global is gross */
  3925.  
  3926. void PromotionPopUp(piece, to_x, to_y)
  3927.      ChessSquare piece;
  3928.      int to_x, to_y;
  3929. {
  3930.     Arg args[2];
  3931.     Widget dialog;
  3932.     Position x, y;
  3933.     Dimension bw_width, bw_height, pw_width, pw_height;
  3934.     
  3935.     pmi.piece = piece;
  3936.     pmi.to_x = to_x;
  3937.     pmi.to_y = to_y;
  3938.     
  3939.     XtSetArg(args[0], XtNwidth, &bw_width);
  3940.     XtSetArg(args[1], XtNheight, &bw_height);
  3941.     XtGetValues(boardWidget, args, 2);
  3942.     
  3943.     XtSetArg(args[0], XtNresizable, True);
  3944.     promotionShell =
  3945.       XtCreatePopupShell("Promotion", transientShellWidgetClass,
  3946.              commandsWidget, args, 1);
  3947.     
  3948.     XtSetArg(args[0], XtNlabel, "Promote pawn to what?");
  3949.     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
  3950.                    promotionShell, args, 1);
  3951.     
  3952.     XawDialogAddButton(dialog, "Queen", PromotionCallback, 
  3953.                (XtPointer) dialog);
  3954.     XawDialogAddButton(dialog, "Rook", PromotionCallback, 
  3955.                (XtPointer) dialog);
  3956.     XawDialogAddButton(dialog, "Bishop", PromotionCallback, 
  3957.                (XtPointer) dialog);
  3958.     XawDialogAddButton(dialog, "Knight", PromotionCallback, 
  3959.                (XtPointer) dialog);
  3960.     XawDialogAddButton(dialog, "cancel", PromotionCallback, 
  3961.                (XtPointer) dialog);
  3962.     
  3963.     XtRealizeWidget(promotionShell);
  3964.     
  3965.     XtSetArg(args[0], XtNwidth, &pw_width);
  3966.     XtSetArg(args[1], XtNheight, &pw_height);
  3967.     XtGetValues(promotionShell, args, 2);
  3968.     
  3969.     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
  3970.               LINE_GAP + squareSize/3 +
  3971.               ((piece == WhitePawn) ^ (flipView) ?
  3972.                0 : 6*(squareSize + LINE_GAP)), &x, &y);
  3973.     
  3974.     XtSetArg(args[0], XtNx, x);
  3975.     XtSetArg(args[1], XtNy, y);
  3976.     XtSetValues(promotionShell, args, 2);
  3977.     
  3978.     XtPopup(promotionShell, XtGrabNone);
  3979.     
  3980.     promotionUp = True;
  3981. }
  3982.  
  3983. void PromotionCallback(w, client_data, call_data)
  3984.      Widget w;
  3985.      XtPointer client_data, call_data;
  3986. {
  3987.     String name;
  3988.     Arg args[1];
  3989.     ChessMove move_type;
  3990.     int promoChar;
  3991.     
  3992.     XtSetArg(args[0], XtNlabel, &name);
  3993.     XtGetValues(w, args, 1);
  3994.     
  3995.     XtPopdown(w = XtParent(XtParent(w)));
  3996.     XtDestroyWidget(w);
  3997.     promotionUp = False;
  3998.     
  3999.     if (fromX == -1) return;
  4000.     
  4001.     if (strcmp(name, "cancel") == 0) {
  4002.     fromX = fromY = -1;
  4003.     return;
  4004.     } else if (strcmp(name, "Knight") == 0) {
  4005.     promoChar = 'n';
  4006.     } else {
  4007.     promoChar = ToLower(name[0]);
  4008.     }
  4009.  
  4010.     move_type = LegalityTest(WhiteOnMove(forwardMostMove),
  4011.                  boards[forwardMostMove], fromY, fromX,
  4012.                  pmi.to_y, pmi.to_x, promoChar);
  4013.     if (move_type == BadMove) {
  4014.     DisplayMessage("Illegal move");
  4015.     fromX = fromY = -1;
  4016.     return;
  4017.     }
  4018.     MakeMove(&move_type, fromX, fromY, pmi.to_x, pmi.to_y);
  4019.     FinishUserMove(move_type, pmi.to_x, pmi.to_y);
  4020. }
  4021.  
  4022. void SelectCommand(w, client_data, call_data)
  4023.      Widget w;
  4024.      XtPointer client_data, call_data;
  4025. {
  4026.     XawListReturnStruct *list_return = XawListShowCurrent(w);
  4027.     
  4028.     fromX = fromY = -1;
  4029.     
  4030.     if (promotionUp) {
  4031.     XtPopdown(promotionShell);
  4032.     XtDestroyWidget(promotionShell);
  4033.     promotionUp = False;
  4034.     }
  4035.     
  4036.     (*buttonProcs[list_return->list_index])(w, NULL, NULL, NULL);
  4037.     
  4038.     if (!filenameUp) ModeHighlight();
  4039. }
  4040.  
  4041. void HighlightProcButton(proc)
  4042.      XtActionProc proc;
  4043. {
  4044.     int i = 0;
  4045.     
  4046.     if (proc == NULL) {
  4047.     XawListUnhighlight(commandsWidget);
  4048.     return;
  4049.     }
  4050.     
  4051.     for (;;) {
  4052.     if (buttonProcs[i] == NULL) {
  4053.         XawListUnhighlight(commandsWidget);
  4054.         return;
  4055.     }
  4056.     if (buttonProcs[i] == proc) {
  4057.         XawListHighlight(commandsWidget, i);
  4058.         return;
  4059.     }
  4060.     i++;
  4061.     }
  4062. }
  4063.  
  4064. void ModeHighlight()
  4065. {
  4066.     switch (gameMode) {
  4067.       case BeginningOfGame:
  4068.     if (appData.icsActive)
  4069.       HighlightProcButton(NULL);
  4070.     else if (appData.noChessProgram)
  4071.       HighlightProcButton(ForceProc);
  4072.     else
  4073.       HighlightProcButton(MachineBlackProc);
  4074.     break;
  4075.       case MachinePlaysBlack:
  4076.     HighlightProcButton(MachineBlackProc);
  4077.     break;
  4078.       case MachinePlaysWhite:
  4079.     HighlightProcButton(MachineWhiteProc);
  4080.     break;
  4081.       case TwoMachinesPlay:
  4082.     HighlightProcButton(TwoMachinesProc);
  4083.     break;
  4084.       case ForceMoves:
  4085.     if (appData.icsActive)
  4086.       HighlightProcButton(NULL);
  4087.     else
  4088.       HighlightProcButton(ForceProc);
  4089.     break;
  4090.       case PlayFromGameFile:
  4091.     HighlightProcButton(LoadGameProc);
  4092.     break;
  4093.       case PauseGame:
  4094.     HighlightProcButton(PauseProc);
  4095.     break;
  4096.       case EditPosition:
  4097.     HighlightProcButton(EditPositionProc);
  4098.     break;
  4099.       case EndOfGame:
  4100.       default:
  4101.     HighlightProcButton(NULL);
  4102.     break;
  4103.     }
  4104. }
  4105.  
  4106. /*
  4107.  * Button procedures
  4108.  */
  4109. void QuitProc(w, event, prms, nprms)
  4110.      Widget w;
  4111.      XEvent *event;
  4112.      String *prms;
  4113.      Cardinal *nprms;
  4114. {
  4115.     /* Save game if resource set and not already saved */
  4116.     if (*endMessage == NULLCHAR) {
  4117.     if (appData.saveGameFile[0] != NULLCHAR) 
  4118.       SaveGame(appData.saveGameFile);
  4119.     if (appData.savePositionFile[0] != NULLCHAR) 
  4120.       SavePosition(appData.savePositionFile);
  4121.     }
  4122.     ShutdownChessPrograms(NULL);
  4123.     if (telnetPID != 0) {
  4124.     if (kill(telnetPID, SIGTERM) == 0)
  4125.       wait((union wait *) 0);
  4126.     }
  4127.     exit(0);
  4128. }
  4129.  
  4130. void CallFlagProc(w, event, prms, nprms)
  4131.      Widget w;
  4132.      XEvent *event;
  4133.      String *prms;
  4134.      Cardinal *nprms;
  4135. {
  4136.     /* Call your opponent's flag (claim a win on time) */
  4137.     if (appData.icsActive) {
  4138.     SendToICS("flag\n");
  4139.     } else {
  4140.     switch (gameMode) {
  4141.       default:
  4142.         return;
  4143.       case MachinePlaysWhite:
  4144.         if (whiteFlag) {
  4145.         if (blackFlag)
  4146.           ShutdownChessPrograms("Draw (both players ran out of time)");
  4147.         else
  4148.           ShutdownChessPrograms("Black wins on time");
  4149.         }
  4150.         break;
  4151.       case MachinePlaysBlack:
  4152.         if (blackFlag) {
  4153.         if (whiteFlag)
  4154.           ShutdownChessPrograms("Draw (both players ran out of time)");
  4155.         else
  4156.           ShutdownChessPrograms("White wins on time");
  4157.         }
  4158.         break;
  4159.     }
  4160.     }
  4161. }
  4162.  
  4163. void DrawProc(w, event, prms, nprms)
  4164.      Widget w;
  4165.      XEvent *event;
  4166.      String *prms;
  4167.      Cardinal *nprms;
  4168. {
  4169.     /* Offer draw or accept pending draw offer from opponent */
  4170.     
  4171.     if (appData.icsActive) {
  4172.     /* Note: tournament rules require draw offers to be
  4173.        made after you make your move but before you punch
  4174.        your clock.  Currently ICS doesn't let you do that;
  4175.        instead, you always punch your clock after making a
  4176.        move, but you can offer a draw at any time. */
  4177.     
  4178.     SendToICS("draw\n");
  4179.     } else {
  4180.     /* Currently GNU Chess doesn't offer or accept draws
  4181.        at all, so there is no Draw button in GNU Chess
  4182.        mode.  */
  4183.     
  4184.     fprintf(stderr, "Draw function not implemented\n");
  4185.     }
  4186. }
  4187.  
  4188.  
  4189. void DeclineDrawProc(w, event, prms, nprms)
  4190.      Widget w;
  4191.      XEvent *event;
  4192.      String *prms;
  4193.      Cardinal *nprms;
  4194. {
  4195.     /* Decline a pending draw offer from opponent */
  4196.     
  4197.     if (appData.icsActive) {
  4198.     /* Note: ICS also lets you withdraw your own draw
  4199.        offer with this command.  I'm not sure how long
  4200.        your draw offer remains pending if you don't
  4201.        withdraw it. */
  4202.     
  4203.     SendToICS("decline draw\n");
  4204.     } else {
  4205.     /* Currently GNU Chess doesn't offer or accept draws
  4206.        at all, so there is no Decline Draw button in
  4207.        GNU Chess mode.  */
  4208.     
  4209.     fprintf(stderr, "Decline Draw function not implemented\n");
  4210.     }
  4211. }
  4212.  
  4213.  
  4214. void ResignProc(w, event, prms, nprms)
  4215.      Widget w;
  4216.      XEvent *event;
  4217.      String *prms;
  4218.      Cardinal *nprms;
  4219. {
  4220.     /* Resign.  You can do this even if it's not your turn. */
  4221.     
  4222.     if (appData.icsActive) {
  4223.     SendToICS("resign\n");
  4224.     } else {
  4225.     /* This button is not currently used in GNU Chess mode,
  4226.        but it should work. */
  4227.     
  4228.     switch (gameMode) {
  4229.       case MachinePlaysWhite:
  4230.         ShutdownChessPrograms("Black resigns");
  4231.         break;
  4232.       case MachinePlaysBlack:
  4233.         ShutdownChessPrograms("White resigns");
  4234.         break;
  4235.       default:
  4236.         break;
  4237.     }
  4238.     }
  4239. }
  4240.  
  4241.  
  4242. Boolean LoadGame(name)
  4243.      char *name;
  4244. {
  4245.     char buf[MSG_SIZ], lname[MSG_SIZ];
  4246.     ChessMove cm;
  4247.     int gameNumber;
  4248.     char *p;
  4249.     
  4250.     if (gameMode != BeginningOfGame) {
  4251.     DisplayMessage("Press Reset first");
  4252.     return False;
  4253.     }
  4254.     
  4255.     /* Parse game number if given */
  4256.     strcpy(lname, name);
  4257.     p = strrchr(lname, ' ');
  4258.     if (p == NULL) {
  4259.     gameNumber = 1;
  4260.     } else {
  4261.     *p++ = NULLCHAR;
  4262.     gameNumber = atoi(p);
  4263.     }
  4264.  
  4265.     yynewfile();
  4266.     if ((gameFileFP = fopen(lname, "r")) == NULL) {
  4267.     sprintf(buf, "Can't open %s", lname);
  4268.     DisplayMessage(buf);
  4269.     return False;
  4270.     }
  4271.     
  4272.     p = strrchr(name, '/');
  4273.     if (p == NULL)
  4274.       p = name;
  4275.     else
  4276.       p++;
  4277.     DisplayTitle(p);
  4278.  
  4279.     lastGameMode = gameMode = PlayFromGameFile;
  4280.     ModeHighlight();
  4281.     InitPosition(True);
  4282.     DisplayClocks(StopTimers);
  4283.     if (firstProgramXID == 0) {
  4284.     InitChessProgram(appData.firstHost, appData.firstChessProgram,
  4285.              &firstProgramPID, &toFirstProgFP,
  4286.              &fromFirstProgFP, &firstProgramXID,
  4287.              &firstSendTime);
  4288.     } else {
  4289.     SendToProgram(appData.initString, toFirstProgFP);
  4290.     SendSearchDepth(toFirstProgFP);
  4291.     }
  4292.     SendToProgram("force\n", toFirstProgFP);
  4293.     
  4294.     /*
  4295.      * Skip the first gameNumber-1 games in the file.
  4296.      * Also skip over anything that precedes an identifiable 
  4297.      * StartGame marker, to avoid being confused by 
  4298.      * garbage at the start of the file.  Currently 
  4299.      * recognized StartGame markers are the move number "1",
  4300.      * the pattern "gnuchess .* game", or a position diagram.
  4301.      */
  4302.     cm = StartGame;
  4303.     while (gameNumber > 0) {
  4304.     yyboardindex = forwardMostMove;
  4305.     cm = (ChessMove) yylex();
  4306.     if (cm == (ChessMove) 0) {
  4307.         Reset(True);
  4308.         DisplayMessage("Game not found in file");
  4309.         return False;
  4310.     }
  4311.     if (cm == StartGame || cm == PositionDiagram)
  4312.       gameNumber--;
  4313.     }
  4314.     
  4315.     /* If the file starts with a position diagram,
  4316.        set up the position */
  4317.     if (cm == PositionDiagram) {
  4318.     Board initial_position;
  4319.     int i, j;
  4320.     char *p = yytext;
  4321.     
  4322.     startedFromSetupPosition = True;
  4323.     
  4324.     for (i = BOARD_SIZE - 1; i >= 0; i--)
  4325.       for (j = 0; j < BOARD_SIZE; p++)
  4326.         switch (*p) {
  4327.           case '[':
  4328.           case '-':
  4329.           case ' ':
  4330.           case '\t':
  4331.           case '\n':
  4332.         break;
  4333.           default:
  4334.         initial_position[i][j++] = CharToPiece(*p);
  4335.         break;
  4336.         }
  4337.     
  4338.     while (*p == ' ' || *p == '\t' || *p == '\n') p++;
  4339.     
  4340.     if (strncmp(p, "black", strlen("black"))==0)
  4341.       blackPlaysFirst = True;
  4342.     else
  4343.       blackPlaysFirst = False;
  4344.     
  4345.     if (blackPlaysFirst) {
  4346.         CopyBoard(boards[0], initial_position);
  4347.         strcpy(moveList[0], "");
  4348.         strcpy(parseList[0], "");
  4349.         currentMove = forwardMostMove = backwardMostMove = 1;
  4350.         CopyBoard(boards[1], initial_position);
  4351.         SendToProgram("a3\n", toFirstProgFP);
  4352.         SendCurrentBoard(toFirstProgFP);
  4353.     } else {
  4354.         currentMove = forwardMostMove = backwardMostMove = 0;
  4355.         CopyBoard(boards[0], initial_position);
  4356.         SendCurrentBoard(toFirstProgFP);
  4357.     }
  4358.     
  4359.     DrawPosition(boardWidget, NULL, NULL, NULL);
  4360.     }
  4361.  
  4362.     DisplayClocks(ResetTimers);
  4363.     timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
  4364.     timeRemaining[1][forwardMostMove] = blackTimeRemaining;
  4365.  
  4366.     LoadGameLoop();
  4367.     
  4368.     return True;
  4369. }
  4370.  
  4371. void ResurrectChessProgram()
  4372.      /* Get out of EndOfGame mode.  This may require us to restart the
  4373.     chess program and feed it all the moves made so far. */
  4374. {
  4375.     char buf[MSG_SIZ];
  4376.     int i;
  4377.     
  4378.     /* assert(gameMode == EndOfGame) */
  4379.     
  4380.     gameMode = lastGameMode = ForceMoves;
  4381.     ModeHighlight();
  4382.     
  4383.     if (firstProgramPID != 0) return;
  4384.     
  4385.     InitChessProgram(appData.firstHost, appData.firstChessProgram,
  4386.              &firstProgramPID, &toFirstProgFP, &fromFirstProgFP,
  4387.              &firstProgramXID, &firstSendTime);
  4388.     SendToProgram("force\n", toFirstProgFP);
  4389.     
  4390.     if (startedFromSetupPosition) {
  4391.     if (backwardMostMove % 2 == 1)
  4392.       SendToProgram("a3\n", toFirstProgFP);
  4393.     SendBoard(toFirstProgFP, boards[backwardMostMove]);
  4394.     }
  4395.     
  4396.     for (i = backwardMostMove; i < currentMove; i++) {
  4397.     strcpy(buf, moveList[i]);
  4398.     SendToProgram(buf, toFirstProgFP);
  4399.     }
  4400.     
  4401.     if (!firstSendTime) {
  4402.     /* can't tell gnuchess what its clock should read,
  4403.        so we bow to its notion. */
  4404.     DisplayClocks(ResetTimers);
  4405.     timeRemaining[0][currentMove] = whiteTimeRemaining;
  4406.     timeRemaining[1][currentMove] = blackTimeRemaining;
  4407.     }
  4408. }
  4409.  
  4410. void MachineBlackProc(w, event, prms, nprms)
  4411.      Widget w;
  4412.      XEvent *event;
  4413.      String *prms;
  4414.      Cardinal *nprms;
  4415. {
  4416.     if (gameMode == PauseGame) PauseProc(w, event, prms, nprms);
  4417.     if (gameMode == PlayFromGameFile) ForceProc(w, event, prms, nprms);
  4418.     if (gameMode == EditPosition) EditPositionDone();
  4419.     
  4420.     if ((gameMode == EndOfGame) || (gameMode == TwoMachinesPlay) ||
  4421.     (appData.noChessProgram) || (gameMode == MachinePlaysBlack))
  4422.       return;
  4423.     
  4424.     if (WhiteOnMove(gameMode == ForceMoves ? currentMove : forwardMostMove)) {
  4425.     DisplayMessage("It is not Black's turn");
  4426.     return;
  4427.     }
  4428.     
  4429.     if (gameMode == ForceMoves) forwardMostMove = currentMove;
  4430.     
  4431.     lastGameMode = gameMode = MachinePlaysBlack;
  4432.     ModeHighlight();
  4433.     SendToProgram(appData.blackString, toFirstProgFP);
  4434.     DisplayClocks(StartTimers);
  4435. }
  4436.  
  4437. void ForwardProc(w, event, prms, nprms)
  4438.      Widget w;
  4439.      XEvent *event;
  4440.      String *prms;
  4441.      Cardinal *nprms;
  4442. {
  4443.     char buf[MSG_SIZ];
  4444.     int target;
  4445.     unsigned int state;
  4446.     
  4447.     if ((gameMode == EndOfGame) || (gameMode == EditPosition))
  4448.       return;
  4449.     
  4450.     if (gameMode == PlayFromGameFile)
  4451.       PauseProc(w, event, prms, nprms);
  4452.     
  4453.     if (currentMove >= forwardMostMove) {
  4454.     if (gameFileFP != NULL)
  4455.       (void) LoadGameOneMove();
  4456.     return;
  4457.     }
  4458.     
  4459.     if (event == NULL) {
  4460.     /* Kludge */
  4461.     Window root, child;
  4462.     int root_x, root_y;
  4463.     int win_x, win_y;
  4464.     XQueryPointer(xDisplay, xBoardWindow,
  4465.               &root, &child, &root_x, &root_y,
  4466.               &win_x, &win_y, &state);
  4467.     } else {
  4468.     state = event->xkey.state;
  4469.     }
  4470.     
  4471.     if (state & ShiftMask)
  4472.       target = forwardMostMove;
  4473.     else
  4474.       target = currentMove + 1;
  4475.     
  4476.     if (gameMode == ForceMoves) {
  4477.     while (currentMove < target) {
  4478.         strcpy(buf, moveList[currentMove++]);
  4479.         SendToProgram(buf, toFirstProgFP);
  4480.     }
  4481.     } else {
  4482.     currentMove = target;
  4483.     }
  4484.     
  4485.     if (!appData.icsActive && gameMode == ForceMoves) {
  4486.     whiteTimeRemaining = timeRemaining[0][currentMove];
  4487.     blackTimeRemaining = timeRemaining[1][currentMove];
  4488.     }
  4489.     DisplayClocks(ReDisplayTimers);
  4490.     DisplayMove(currentMove - 1);
  4491.     DrawPosition(boardWidget, NULL, NULL, NULL);
  4492. }
  4493.  
  4494.  
  4495. void ResetProc(w, event, prms, nprms)
  4496.      Widget w;
  4497.      XEvent *event;
  4498.      String *prms;
  4499.      Cardinal *nprms;
  4500. {
  4501.     Reset(True);
  4502. }
  4503.  
  4504. void Reset(redraw)
  4505.      int redraw;
  4506. {
  4507.     flipView = False;
  4508.     startedFromSetupPosition = blackPlaysFirst = False;
  4509.     matchMode = MatchFalse;
  4510.     firstMove = True;
  4511.     whiteFlag = blackFlag = False;
  4512.     maybeThinking = False;
  4513.     endMessage[0] = NULLCHAR;
  4514.     ics_white[0] = ics_black[0] = NULLCHAR;
  4515.     ics_user_moved = ics_getting_history = False;
  4516.     ics_mode = IcsIdle;
  4517.     ics_gamenum = -1;
  4518.     
  4519.     ShutdownChessPrograms(NULL);
  4520.     lastGameMode = gameMode = BeginningOfGame;
  4521.     ModeHighlight();
  4522.     InitPosition(redraw);
  4523.     DisplayClocks(ResetTimers);
  4524.     timeRemaining[0][0] = whiteTimeRemaining;
  4525.     timeRemaining[1][0] = blackTimeRemaining;
  4526.     InitChessProgram(appData.firstHost, appData.firstChessProgram,
  4527.              &firstProgramPID, &toFirstProgFP,
  4528.              &fromFirstProgFP, &firstProgramXID, &firstSendTime);
  4529.     DisplayTitle("");
  4530.     DisplayMessage("");
  4531.     if (commentUp) CommentPopDown();
  4532.     if (promotionUp) {
  4533.     XtPopdown(promotionShell);
  4534.     XtDestroyWidget(promotionShell);
  4535.     promotionUp = False;
  4536.     }
  4537. }
  4538.  
  4539. void LoadPositionProc(w, event, prms, nprms)
  4540.      Widget w;
  4541.      XEvent *event;
  4542.      String *prms;
  4543.      Cardinal *nprms;
  4544. {
  4545.     if (gameMode != BeginningOfGame) {
  4546.     DisplayMessage("Press Reset first");
  4547.     return;
  4548.     }
  4549.     FileNamePopUp("Position file name?", "", LoadPosition);
  4550. }
  4551.  
  4552. Boolean LoadPosition(name)
  4553.      char *name;
  4554. {
  4555.     char *p, line[MSG_SIZ], buf[MSG_SIZ];
  4556.     Board initial_position;
  4557.     FILE *fp;
  4558.     int i, j, positionNumber;
  4559.     
  4560.     if (gameMode != BeginningOfGame) {
  4561.     DisplayMessage("Press Reset first");
  4562.     return False;
  4563.     }
  4564.     
  4565.     /* Parse position number if given */
  4566.     strcpy(line, name);
  4567.     p = strrchr(line, ' ');
  4568.     if (p == NULL) {
  4569.     positionNumber = 0;
  4570.     } else {
  4571.     *p++ = NULLCHAR;
  4572.     positionNumber = atoi(p);
  4573.     }
  4574.  
  4575.     if ((fp = fopen(line, "r")) == NULL) {
  4576.     sprintf(buf, "Can't open %s", line);
  4577.     DisplayMessage(buf);
  4578.     return False;
  4579.     }
  4580.     
  4581.     p = strrchr(name, '/');
  4582.     if (p == NULL)
  4583.       p = name;
  4584.     else
  4585.       p++;
  4586.     DisplayTitle(p);
  4587.  
  4588.     lastGameMode = gameMode = ForceMoves;
  4589.     ModeHighlight();
  4590.     startedFromSetupPosition = True;
  4591.     
  4592.     if (firstProgramXID == 0)
  4593.       InitChessProgram(appData.firstHost, appData.firstChessProgram,
  4594.                &firstProgramPID, &toFirstProgFP,
  4595.                &fromFirstProgFP, &firstProgramXID, &firstSendTime);
  4596.     
  4597.     if (positionNumber == 0) {
  4598.     /* Backward compatibility---don't look for '#' */
  4599.     (void) fgets(line, MSG_SIZ, fp);
  4600.     } else {
  4601.     while (positionNumber > 0) {
  4602.         /* skip postions before number positionNumber */
  4603.         if (fgets(line, MSG_SIZ, fp) == NULL) {
  4604.         Reset(True);
  4605.         DisplayMessage("Position not found in file");
  4606.         return False;
  4607.         }
  4608.         if (line[0] == '#') positionNumber--;
  4609.     }
  4610.     }
  4611.  
  4612.     (void) fgets(line, MSG_SIZ, fp);
  4613.     (void) fgets(line, MSG_SIZ, fp);
  4614.     
  4615.     for (i = BOARD_SIZE - 1; i >= 0; i--) {
  4616.     (void) fgets(line, MSG_SIZ, fp);
  4617.     for (p = line, j = 0; j < BOARD_SIZE; p++) {
  4618.         if (*p == ' ')
  4619.           continue;
  4620.         initial_position[i][j++] = CharToPiece(*p);
  4621.     }
  4622.     }
  4623.     
  4624.     blackPlaysFirst = False;
  4625.     if (!feof(fp)) {
  4626.     (void) fgets(line, MSG_SIZ, fp);
  4627.     if (strncmp(line, "black", strlen("black"))==0)
  4628.       blackPlaysFirst = True;
  4629.     }
  4630.     fclose(fp);
  4631.     
  4632.     if (blackPlaysFirst) {
  4633.     CopyBoard(boards[0], initial_position);
  4634.     strcpy(moveList[0], "");
  4635.     strcpy(parseList[0], "");
  4636.     currentMove = forwardMostMove = backwardMostMove = 1;
  4637.     CopyBoard(boards[1], initial_position);
  4638.     SendToProgram("force\na3\n", toFirstProgFP);
  4639.     SendCurrentBoard(toFirstProgFP);
  4640.     DisplayMessage("Black to play");
  4641.     } else {
  4642.     currentMove = forwardMostMove = backwardMostMove = 0;
  4643.     CopyBoard(boards[0], initial_position);
  4644.     SendCurrentBoard(toFirstProgFP);
  4645.     SendToProgram("force\n", toFirstProgFP);
  4646.     DisplayMessage("White to play");
  4647.     }
  4648.     
  4649.     DisplayClocks(ResetTimers);
  4650.     timeRemaining[0][1] = whiteTimeRemaining;
  4651.     timeRemaining[1][1] = blackTimeRemaining;
  4652.  
  4653.     DrawPosition(boardWidget, NULL, NULL, NULL);
  4654.     
  4655.     return (int) True;
  4656. }
  4657.  
  4658. void EditPositionProc(w, event, prms, nprms)
  4659.      Widget w;
  4660.      XEvent *event;
  4661.      String *prms;
  4662.      Cardinal *nprms;
  4663. {
  4664.     if (gameMode == EditPosition) {
  4665.     ForceProc(w, event, prms, nprms);
  4666.     return;
  4667.     }
  4668.     
  4669.     ForceProc(w, event, prms, nprms);
  4670.     if (gameMode != ForceMoves) return;
  4671.     
  4672.     DisplayTitle("<-- Press to set side to play next");
  4673.     DisplayMessage("Mouse: 1=drag, 2=white, 3=black");
  4674.     
  4675.     lastGameMode = gameMode = EditPosition;
  4676.     ModeHighlight();
  4677.     if (currentMove > 0)
  4678.       CopyBoard(boards[0], boards[currentMove]);
  4679.     
  4680.     blackPlaysFirst = !WhiteOnMove(currentMove);
  4681.     DisplayClocks(ResetTimers);
  4682.     currentMove = forwardMostMove = backwardMostMove = 0;
  4683. }
  4684.  
  4685. void EditPositionDone()
  4686. {
  4687.     startedFromSetupPosition = True;
  4688.     SendToProgram(appData.initString, toFirstProgFP);
  4689.     SendSearchDepth(toFirstProgFP);
  4690.     if (blackPlaysFirst) {
  4691.     strcpy(moveList[0], "");
  4692.     strcpy(parseList[0], "");
  4693.     currentMove = forwardMostMove = backwardMostMove = 1;
  4694.     CopyBoard(boards[1], boards[0]);
  4695.     SendToProgram("force\na3\n", toFirstProgFP);
  4696.     SendCurrentBoard(toFirstProgFP);
  4697.     DisplayTitle("");
  4698.     DisplayMessage("Black to play");
  4699.     } else {
  4700.     currentMove = forwardMostMove = backwardMostMove = 0;
  4701.     SendCurrentBoard(toFirstProgFP);
  4702.     SendToProgram("force\n", toFirstProgFP);
  4703.     DisplayTitle("");
  4704.     DisplayMessage("White to play");
  4705.     }
  4706.     timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
  4707.     timeRemaining[1][forwardMostMove] = blackTimeRemaining;
  4708.     lastGameMode = gameMode = ForceMoves;
  4709. }
  4710.  
  4711. void MachineWhiteProc(w, event, prms, nprms)
  4712.      Widget w;
  4713.      XEvent *event;
  4714.      String *prms;
  4715.      Cardinal *nprms;
  4716. {
  4717.     if (gameMode == PauseGame) PauseProc(w, event, prms, nprms);
  4718.     if (gameMode == PlayFromGameFile) ForceProc(w, event, prms, nprms);
  4719.     if (gameMode == EditPosition) EditPositionDone();
  4720.     
  4721.     if ((gameMode == EndOfGame) || (gameMode == TwoMachinesPlay) ||
  4722.     (appData.noChessProgram) || (gameMode == MachinePlaysWhite))
  4723.       return;
  4724.     
  4725.     if (!WhiteOnMove(gameMode == ForceMoves ? currentMove : forwardMostMove)) {
  4726.     DisplayMessage("It is not White's turn");
  4727.     return;
  4728.     }
  4729.     
  4730.     if (gameMode == ForceMoves) forwardMostMove = currentMove;
  4731.     
  4732.     lastGameMode = gameMode = MachinePlaysWhite;
  4733.     ModeHighlight();
  4734.     SendToProgram(appData.whiteString, toFirstProgFP);
  4735.     DisplayClocks(StartTimers);
  4736. }
  4737.  
  4738. void BackwardProc(w, event, prms, nprms)
  4739.      Widget w;
  4740.      XEvent *event;
  4741.      String *prms;
  4742.      Cardinal *nprms;
  4743. {
  4744.     int target;
  4745.     unsigned int state;
  4746.     
  4747.     if ((currentMove <= backwardMostMove) || (gameMode == EditPosition))
  4748.       return;
  4749.     
  4750.     if (gameMode == EndOfGame)
  4751.       ResurrectChessProgram();
  4752.     
  4753.     if (gameMode == PlayFromGameFile)
  4754.       PauseProc(w, event, prms, nprms);
  4755.     
  4756.     if (event == NULL) {
  4757.     /* Kludge */
  4758.     Window root, child;
  4759.     int root_x, root_y;
  4760.     int win_x, win_y;
  4761.     XQueryPointer(xDisplay, xBoardWindow,
  4762.               &root, &child, &root_x, &root_y,
  4763.               &win_x, &win_y, &state);
  4764.     } else {
  4765.     state = event->xkey.state;
  4766.     }
  4767.     if (state & ShiftMask)
  4768.       target = backwardMostMove;
  4769.     else
  4770.       target = currentMove - 1;
  4771.     
  4772.     if (gameMode == ForceMoves) {
  4773.     Attention(firstProgramPID);
  4774.     while (currentMove > target) {
  4775.         SendToProgram("undo\n", toFirstProgFP);
  4776.         currentMove--;
  4777.     }
  4778.     } else {
  4779.     currentMove = target;
  4780.     }
  4781.     
  4782.     if (!appData.icsActive && gameMode == ForceMoves) {
  4783.     whiteTimeRemaining = timeRemaining[0][currentMove];
  4784.     blackTimeRemaining = timeRemaining[1][currentMove];
  4785.     }
  4786.     DisplayClocks(ReDisplayTimers);
  4787.     DisplayMove(currentMove - 1);
  4788.     DrawPosition(boardWidget, NULL, NULL, NULL);
  4789. }
  4790.  
  4791. void FlipViewProc(w, event, prms, nprms)
  4792.      Widget w;
  4793.      XEvent *event;
  4794.      String *prms;
  4795.      Cardinal *nprms;
  4796. {
  4797.     flipView = !flipView;
  4798.     DrawPosition(boardWidget, NULL, NULL, NULL);
  4799. }
  4800.  
  4801. void SaveGameProc(w, event, prms, nprms)
  4802.      Widget w;
  4803.      XEvent *event;
  4804.      String *prms;
  4805.      Cardinal *nprms;
  4806. {
  4807.     char def[MSG_SIZ];
  4808.     
  4809.     if (appData.icsActive && ics_white[0] != NULLCHAR) {
  4810.     sprintf(def, "%s-%s.game", ics_white, ics_black);
  4811.     } else {
  4812.     def[0] = NULLCHAR;
  4813.     }
  4814.     FileNamePopUp("Filename for saved game?", def, SaveGame);
  4815. }
  4816.  
  4817. Boolean SaveGame(name)
  4818.      char *name;
  4819. {
  4820.     char buf[MSG_SIZ];
  4821.     int i;
  4822.     time_t tm;
  4823.     
  4824.     if ((gameFileFP = fopen(name, "a")) == NULL) {
  4825.     sprintf(buf, "Can't open %s", name);
  4826.     DisplayMessage(buf);
  4827.     return False;
  4828.     }
  4829.     
  4830.     tm = time((time_t *) NULL);
  4831.     
  4832.     fprintf(gameFileFP, "# %s game file -- %s", programName, ctime(&tm));
  4833.     PrintOpponents(gameFileFP);
  4834.     fprintf(gameFileFP, "\talgebraic\n");
  4835.     
  4836.     if (backwardMostMove > 0 || startedFromSetupPosition) {
  4837.     fprintf(gameFileFP, "[--------------\n");
  4838.     PrintPosition(gameFileFP, backwardMostMove);
  4839.     fprintf(gameFileFP, "--------------]\n");
  4840.     }
  4841.     i = backwardMostMove;
  4842.     if ((i % 2) == 1 && i < forwardMostMove) {
  4843.     fprintf(gameFileFP, "%d. ...  %s\n", i/2 + 1, parseList[i]);
  4844.     i++;
  4845.     }
  4846.     while (i < forwardMostMove) {
  4847.     fprintf(gameFileFP, "%d. %s  ", i/2 + 1, parseList[i]);
  4848.     i++;
  4849.     if (i >= forwardMostMove) {
  4850.         fprintf(gameFileFP, "\n");
  4851.         break;
  4852.     }
  4853.     fprintf(gameFileFP, "%s\n", parseList[i]);
  4854.     i++;
  4855.     }
  4856.     
  4857.     if (endMessage[0] != NULLCHAR)
  4858.       fprintf(gameFileFP, "%s\n", endMessage);
  4859.     
  4860.     fclose(gameFileFP);
  4861.     gameFileFP = NULL;
  4862.     yynewfile();
  4863.     return True;
  4864. }
  4865.  
  4866. #ifdef notdef  
  4867. /* currently unused */
  4868. void SwitchProc(w, event, prms, nprms)
  4869.      Widget w;
  4870.      XEvent *event;
  4871.      String *prms;
  4872.      Cardinal *nprms;
  4873. {
  4874.     if (appData.noChessProgram) return;
  4875.     if (gameMode == PauseGame) PauseProc(w, event);
  4876.     switch (gameMode) {
  4877.       default:
  4878.     return;
  4879.       case MachinePlaysWhite:
  4880.     if (WhiteOnMove(forwardMostMove)) {
  4881.         DisplayMessage("Wait until your turn");
  4882.         return;
  4883.     }
  4884.     lastGameMode = gameMode = MachinePlaysBlack;
  4885.     ModeHighlight();
  4886.     break;
  4887.       case BeginningOfGame:
  4888.       case MachinePlaysBlack:
  4889.     if (!WhiteOnMove(forwardMostMove)) {
  4890.         DisplayMessage("Wait until your turn");
  4891.         return;
  4892.     }
  4893.     if (forwardMostMove == 0) {
  4894.         MachineWhiteProc(w, event);
  4895.         return;
  4896.     }
  4897.     lastGameMode = gameMode = MachinePlaysWhite;
  4898.     ModeHighlight();
  4899.     break;
  4900.     }
  4901.     
  4902.     Attention(firstProgramPID);
  4903.     SendToProgram("switch\n", toFirstProgFP);
  4904. }
  4905. #endif /*notdef*/
  4906.  
  4907. void ForceProc(w, event, prms, nprms)
  4908.      Widget w;
  4909.      XEvent *event;
  4910.      String *prms;
  4911.      Cardinal *nprms;
  4912. {
  4913.     int i;
  4914.     
  4915.     if (gameMode == PauseGame) PauseProc(w, event, prms, nprms);
  4916.     switch (gameMode) {
  4917.       case MachinePlaysWhite:
  4918.     if (WhiteOnMove(forwardMostMove)) {
  4919.         DisplayMessage("Wait until your turn");
  4920.         return;
  4921.     }
  4922.     Attention(firstProgramPID);
  4923.     SendToProgram("force\n", toFirstProgFP);
  4924.     break;
  4925.       case MachinePlaysBlack:
  4926.     if (!WhiteOnMove(forwardMostMove)) {
  4927.         DisplayMessage("Wait until your turn");
  4928.         return;
  4929.     }
  4930.     Attention(firstProgramPID);
  4931.     SendToProgram("force\n", toFirstProgFP);
  4932.     break;
  4933.       case BeginningOfGame:
  4934.     SendToProgram("force\n", toFirstProgFP);
  4935.     break;
  4936.       case PlayFromGameFile:
  4937.     if (readGameXID != 0) {
  4938.         XtRemoveTimeOut(readGameXID);
  4939.         readGameXID = 0;
  4940.     }
  4941.     if (gameFileFP != NULL) {
  4942.         fclose(gameFileFP);
  4943.         gameFileFP = NULL;
  4944.         yynewfile();
  4945.     }
  4946.     break;
  4947.       case EndOfGame:
  4948.     ResurrectChessProgram();
  4949.     return;
  4950.       case EditPosition:
  4951.     EditPositionDone();
  4952.     break;
  4953.       case TwoMachinesPlay:
  4954.     ShutdownChessPrograms("");
  4955.     ResurrectChessProgram();
  4956.     return;
  4957.       default:
  4958.     return;
  4959.     }
  4960.     
  4961.     if (gameMode == MachinePlaysWhite ||
  4962.     gameMode == MachinePlaysBlack ||
  4963.     gameMode == TwoMachinesPlay ||
  4964.     gameMode == PlayFromGameFile) {
  4965.     i = forwardMostMove;
  4966.     while (i > currentMove) {
  4967.         SendToProgram("undo\n", toFirstProgFP);
  4968.         i--;
  4969.     }
  4970.     whiteTimeRemaining = timeRemaining[0][currentMove];
  4971.     blackTimeRemaining = timeRemaining[1][currentMove];
  4972.     if (whiteFlag || blackFlag) {
  4973.         whiteFlag = blackFlag = 0;
  4974.     }
  4975.     DisplayTitle("");
  4976.     }        
  4977.     
  4978.     lastGameMode = gameMode = ForceMoves;
  4979.     ModeHighlight();
  4980.     
  4981.     DisplayClocks(StopTimers);
  4982. }
  4983.  
  4984. void NothingProc(w, event, prms, nprms)
  4985.      Widget w;
  4986.      XEvent *event;
  4987.      String *prms;
  4988.      Cardinal *nprms;
  4989. {
  4990.     return;
  4991. }
  4992.  
  4993. void HintProc(w, event, prms, nprms)
  4994.      Widget w;
  4995.      XEvent *event;
  4996.      String *prms;
  4997.      Cardinal *nprms;
  4998. {
  4999.     if (appData.noChessProgram) return;
  5000.     switch (gameMode) {
  5001.       case MachinePlaysWhite:
  5002.     if (WhiteOnMove(forwardMostMove)) {
  5003.         DisplayMessage("Wait until your turn");
  5004.         return;
  5005.     }
  5006.     break;
  5007.       case BeginningOfGame:
  5008.       case MachinePlaysBlack:
  5009.     if (!WhiteOnMove(forwardMostMove)) {
  5010.         DisplayMessage("Wait until your turn");
  5011.         return;
  5012.     }
  5013.     break;
  5014.       default:
  5015.     DisplayMessage("No hint available");
  5016.     return;
  5017.     }
  5018.     Attention(firstProgramPID);
  5019.     SendToProgram("hint\n", toFirstProgFP);
  5020. }
  5021.  
  5022. void PrintPosition(fp, move)
  5023.      FILE *fp;
  5024.      int move;
  5025. {
  5026.     int i, j;
  5027.     
  5028.     for (i = BOARD_SIZE - 1; i >= 0; i--) {
  5029.     for (j = 0; j < BOARD_SIZE; j++) {
  5030.         fprintf(fp, "%c", PieceToChar(boards[move][i][j]));
  5031.         fputc(j == BOARD_SIZE - 1 ? '\n' : ' ', fp);
  5032.     }
  5033.     }
  5034.     if ((gameMode == EditPosition) ? !blackPlaysFirst : (move % 2 == 0))
  5035.       fprintf(fp, "white to play\n");
  5036.     else
  5037.       fprintf(fp, "black to play\n");
  5038. }
  5039.  
  5040. void PrintOpponents(fp)
  5041.      FILE *fp;
  5042. {
  5043.     char host_name[MSG_SIZ];
  5044.     
  5045. #if defined(SVR4) && defined(sun)
  5046.     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
  5047. #else
  5048.     gethostname(host_name, MSG_SIZ);
  5049. #endif
  5050.     switch (lastGameMode) {
  5051.       case MachinePlaysWhite:
  5052.     fprintf(fp, "\t%s@%s vs. %s@%s\n", appData.firstChessProgram,
  5053.         appData.firstHost, getpwuid(getuid())->pw_name,
  5054.         host_name);
  5055.     break;
  5056.       case MachinePlaysBlack:
  5057.     fprintf(fp, "\t%s@%s vs. %s@%s\n", getpwuid(getuid())->pw_name,
  5058.         host_name, appData.firstChessProgram,
  5059.         appData.firstHost);
  5060.     break;
  5061.       case TwoMachinesPlay:
  5062.     fprintf(fp, "\t%s@%s vs. %s@%s\n", appData.secondChessProgram,
  5063.         appData.secondHost, appData.firstChessProgram,
  5064.         appData.firstHost);
  5065.     break;
  5066.       default:
  5067.     if (appData.icsActive && ics_white[0] != NULLCHAR) {
  5068.         fprintf(fp, "\t%s vs. %s\n",
  5069.             ics_white, ics_black);
  5070.     } else {
  5071.         fprintf(fp, "\n");
  5072.     }
  5073.     break;
  5074.     }
  5075. }
  5076.  
  5077. void SavePositionProc(w, event, prms, nprms)
  5078.      Widget w;
  5079.      XEvent *event;
  5080.      String *prms;
  5081.      Cardinal *nprms;
  5082. {
  5083.     char def[MSG_SIZ];
  5084.     
  5085.     if (appData.icsActive && ics_white[0] != NULLCHAR) {
  5086.     sprintf(def, "%s-%s.pos", ics_white, ics_black);
  5087.     } else {
  5088.     def[0] = NULLCHAR;
  5089.     }
  5090.     FileNamePopUp("Filename for saved position?", def, SavePosition);
  5091. }
  5092.  
  5093. Boolean SavePosition(name)
  5094.      char *name;
  5095. {
  5096.     char buf[MSG_SIZ];
  5097.     FILE *fp;
  5098.     time_t tm;
  5099.     
  5100.     if ((fp = fopen(name, "a")) == NULL) {
  5101.     sprintf(buf, "Can't open %s", name);
  5102.     DisplayMessage(buf);
  5103.     return False;
  5104.     }
  5105.     
  5106.     tm = time((time_t *) NULL);
  5107.     
  5108.     fprintf(fp, "# %s position file -- %s", programName, ctime(&tm));
  5109.     PrintOpponents(fp);
  5110.     fprintf(fp, "\n");
  5111.     PrintPosition(fp, currentMove);
  5112.     fclose(fp);
  5113.     return True;
  5114. }
  5115.  
  5116. void TwoMachinesProc(w, event, prms, nprms)
  5117.      Widget w;
  5118.      XEvent *event;
  5119.      String *prms;
  5120.      Cardinal *nprms;
  5121. {
  5122.     int i;
  5123.     MatchMode matchKind;
  5124.     
  5125.     if (gameMode == PauseGame) PauseProc(w, event, prms, nprms);
  5126.     if (gameMode == PlayFromGameFile) ForceProc(w, event, prms, nprms);
  5127.     if ((gameMode == EndOfGame) || (gameMode == TwoMachinesPlay)
  5128.     || appData.noChessProgram)
  5129.       return;
  5130.     
  5131.     if (matchMode == MatchFalse) {
  5132.     switch (gameMode) {
  5133.       case PauseGame:
  5134.       case PlayFromGameFile:
  5135.         return;
  5136.       case MachinePlaysWhite:
  5137.       case MachinePlaysBlack:
  5138.         ForceProc(w, event, prms, nprms);
  5139.         if (gameMode != ForceMoves) return;
  5140.         matchKind = MatchOpening;
  5141.         break;
  5142.       case ForceMoves:
  5143.         matchKind = MatchOpening;
  5144.         break;
  5145.       case EditPosition:
  5146.         EditPositionDone();
  5147.         matchKind = MatchPosition;
  5148.         break;
  5149.       case BeginningOfGame:
  5150.       default:
  5151.         matchKind = MatchInit;
  5152.         break;
  5153.     }
  5154.     } else {
  5155.     matchKind = matchMode;
  5156.     }
  5157.     
  5158.     forwardMostMove = currentMove;
  5159.     
  5160.     flipView = False;
  5161.     firstMove = False;
  5162.     
  5163.     switch (matchKind) {
  5164.       case MatchOpening:
  5165.     if (firstProgramXID == 0) {
  5166.         if (*appData.loadGameFile == NULLCHAR) {
  5167.         DisplayMessage("You need to specify a game file");
  5168.         return;
  5169.         }
  5170.         InitChessProgram(appData.firstHost,
  5171.                  appData.firstChessProgram,
  5172.                  &firstProgramPID, &toFirstProgFP,
  5173.                  &fromFirstProgFP, &firstProgramXID,
  5174.                  &firstSendTime);
  5175.         if (!LoadGame(appData.loadGameFile)) {
  5176.         ShutdownChessPrograms("Bad game file");
  5177.         return;
  5178.         }
  5179.         DrawPosition(boardWidget, NULL, NULL, NULL);
  5180.     }
  5181.     InitChessProgram(appData.secondHost,
  5182.              appData.secondChessProgram,
  5183.              &secondProgramPID, &toSecondProgFP,
  5184.              &fromSecondProgFP, &secondProgramXID,
  5185.              &secondSendTime);
  5186.     if (startedFromSetupPosition) {
  5187.         if (blackPlaysFirst) {
  5188.         SendToProgram("force\na3\n", toSecondProgFP);
  5189.         SendBoard(toSecondProgFP,
  5190.               boards[backwardMostMove]);
  5191.         } else {
  5192.         SendBoard(toSecondProgFP,
  5193.               boards[backwardMostMove]);
  5194.         SendToProgram("force\n", toSecondProgFP);
  5195.         }
  5196.     } else {
  5197.         SendToProgram("force\n", toSecondProgFP);
  5198.     }
  5199.     for (i = backwardMostMove; i < forwardMostMove; i++)
  5200.       SendToProgram(moveList[i], toSecondProgFP);
  5201.     lastGameMode = gameMode = TwoMachinesPlay;
  5202.     ModeHighlight();
  5203.     firstMove = True;
  5204.     if (WhiteOnMove(forwardMostMove))
  5205.       SendToProgram(appData.whiteString, toSecondProgFP);
  5206.     else
  5207.       SendToProgram(appData.blackString, toFirstProgFP);
  5208.     break;
  5209.     
  5210.       case MatchPosition:
  5211.     if (firstProgramXID == 0) {
  5212.         if (*appData.loadPositionFile == NULLCHAR) {
  5213.         DisplayMessage("You need to specify a position file");
  5214.         return;
  5215.         }
  5216.         InitChessProgram(appData.firstHost,
  5217.                  appData.firstChessProgram,
  5218.                  &firstProgramPID, &toFirstProgFP,
  5219.                  &fromFirstProgFP, &firstProgramXID,
  5220.                  &firstSendTime);
  5221.         if (!LoadPosition(appData.loadPositionFile))
  5222.           return;
  5223.     }
  5224.     InitChessProgram(appData.secondHost,
  5225.              appData.secondChessProgram,
  5226.              &secondProgramPID, &toSecondProgFP,
  5227.              &fromSecondProgFP, &secondProgramXID,
  5228.              &secondSendTime);
  5229.     if (blackPlaysFirst)
  5230.       SendToProgram("force\na3\n", toSecondProgFP);
  5231.     SendCurrentBoard(toSecondProgFP);
  5232.     lastGameMode = gameMode = TwoMachinesPlay;
  5233.     ModeHighlight();
  5234.     firstMove = True;
  5235.     if (WhiteOnMove(forwardMostMove))
  5236.       SendToProgram(appData.whiteString, toSecondProgFP);
  5237.     else
  5238.       SendToProgram(appData.blackString, toFirstProgFP);
  5239.     break;
  5240.     
  5241.       case MatchInit:
  5242.     InitPosition(True);
  5243.     if (firstProgramXID == 0)
  5244.       InitChessProgram(appData.firstHost,
  5245.                appData.firstChessProgram,
  5246.                &firstProgramPID, &toFirstProgFP,
  5247.                &fromFirstProgFP, &firstProgramXID,
  5248.                &firstSendTime);
  5249.     InitChessProgram(appData.secondHost,
  5250.              appData.secondChessProgram,
  5251.              &secondProgramPID, &toSecondProgFP,
  5252.              &fromSecondProgFP, &secondProgramXID,
  5253.              &secondSendTime);
  5254.     lastGameMode = gameMode = TwoMachinesPlay;
  5255.     ModeHighlight();
  5256.     SendToProgram(appData.whiteString, toSecondProgFP);
  5257.     
  5258.       default:
  5259.     break;
  5260.     }
  5261.     
  5262.     if (!firstSendTime || !secondSendTime) {
  5263.     DisplayClocks(ResetTimers);
  5264.     timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
  5265.     timeRemaining[1][forwardMostMove] = blackTimeRemaining;
  5266.     }
  5267.     DisplayClocks(StartTimers);
  5268. }
  5269.  
  5270. void PauseProc(w, event, prms, nprms)
  5271.      Widget w;
  5272.      XEvent *event;
  5273.      String *prms;
  5274.      Cardinal *nprms;
  5275. {
  5276.     switch (gameMode) {
  5277.       case EndOfGame:
  5278.       case EditPosition:
  5279.       default:
  5280.     return;
  5281.       case ForceMoves:
  5282.     if (appData.icsActive) {
  5283.         pausePreviousMode = gameMode;
  5284.         gameMode = PauseGame;
  5285.         ModeHighlight();
  5286.     }
  5287.     return;
  5288.       case PauseGame:
  5289.     gameMode = pausePreviousMode;
  5290.     ModeHighlight();
  5291.     pausePreviousMode = PauseGame;
  5292.     if (gameMode == MachinePlaysWhite ||
  5293.         gameMode == MachinePlaysBlack) {
  5294.         DisplayClocks(StartTimers);
  5295.     } else {
  5296.         DisplayClocks(ReDisplayTimers);
  5297.     }
  5298.     if (gameMode == PlayFromGameFile &&
  5299.         readGameXID == 0 &&
  5300.         appData.timeDelay >= 0) {
  5301.         readGameXID =
  5302.           XtAppAddTimeOut(appContext,
  5303.                   (int) (1000 * appData.timeDelay),
  5304.                   (XtTimerCallbackProc) LoadGameLoop, NULL);
  5305.     }
  5306.     break;
  5307.       case PlayFromGameFile:
  5308.     if (readGameXID != 0) {
  5309.         XtRemoveTimeOut(readGameXID);
  5310.         readGameXID = 0;
  5311.     }
  5312.     pausePreviousMode = gameMode;
  5313.     gameMode = PauseGame;
  5314.     ModeHighlight();
  5315.     break;
  5316.       case BeginningOfGame:
  5317.       case MachinePlaysWhite:
  5318.       case MachinePlaysBlack:
  5319.       case TwoMachinesPlay:
  5320.     if (forwardMostMove == 0)
  5321.       return;    /* don't pause if no one has moved */
  5322.     if ((gameMode == MachinePlaysWhite &&
  5323.          !WhiteOnMove(forwardMostMove)) ||
  5324.         (gameMode == MachinePlaysBlack &&
  5325.          WhiteOnMove(forwardMostMove))) {
  5326.         DisplayClocks(StopTimers);
  5327.     }
  5328.     pausePreviousMode = gameMode;
  5329.     gameMode = PauseGame;
  5330.     ModeHighlight();
  5331.     break;
  5332.     }
  5333. }
  5334.  
  5335. void LoadGameProc(w, event, prms, nprms)
  5336.      Widget w;
  5337.      XEvent *event;
  5338.      String *prms;
  5339.      Cardinal *nprms;
  5340. {
  5341.     if (gameMode == PlayFromGameFile) {
  5342.     ForceProc(w, event, prms, nprms);
  5343.     return;
  5344.     }
  5345.     if (gameMode != BeginningOfGame) {
  5346.     DisplayMessage("Press Reset first");
  5347.     return;
  5348.     }
  5349.     FileNamePopUp("Game file name?", "", LoadGame);
  5350. }
  5351.  
  5352. void Iconify(w, event, prms, nprms)
  5353.      Widget w;
  5354.      XEvent *event;
  5355.      String *prms;
  5356.      Cardinal *nprms;
  5357. {
  5358.     Arg args[1];
  5359.     
  5360.     fromX = fromY = -1;
  5361.     
  5362.     XtSetArg(args[0], XtNiconic, True);
  5363.     XtSetValues(shellWidget, args, 1);
  5364. }
  5365.  
  5366. void SendToProgram(message, fp)
  5367.      char *message;
  5368.      FILE *fp;
  5369. {
  5370.     if (fp == NULL) return;
  5371.     lastMsgFP = fp;
  5372.     
  5373.     if (appData.debugMode)
  5374.       fprintf(stderr, "Sending to %s: %s\n",
  5375.           fp == toFirstProgFP ? "first" : "second", message);
  5376.     
  5377.     if (message[strlen(message) - 1] != '\n')
  5378.       fprintf(fp, "\n%s\n", message);
  5379.     else
  5380.       fputs(message, fp);
  5381.     fflush(fp);
  5382. }
  5383.  
  5384. void ReceiveFromProgram(fp, source, id)
  5385.      FILE *fp;
  5386.      int *source;
  5387.      XtInputId *id;
  5388. {
  5389.     char message[MSG_SIZ], *end_str, *name, *number;
  5390.     extern char *sys_errlist[];
  5391.  
  5392.     if (fgets(message, MSG_SIZ, fp) == NULL) {
  5393.     if (fp == fromFirstProgFP) {
  5394.         number = "first";
  5395.         name = appData.firstChessProgram;
  5396.     } else if (fp == fromSecondProgFP) {
  5397.         number = "second";
  5398.         name = appData.secondChessProgram;
  5399.     } else {
  5400.         return;
  5401.     }
  5402.     if (ferror(fp) == 0) {
  5403.         sprintf(message, "%s chess program (%s) exited unexpectedly",
  5404.             number, name);
  5405.         fprintf(stderr, "%s: %s\n", programName, message);
  5406.     } else {
  5407.         sprintf(message,
  5408.             "error reading from %s chess program (%s): %s",
  5409.             number, name, sys_errlist[ferror(fp)]);
  5410.         fprintf(stderr, "%s: %s\n", programName, message);
  5411.     }
  5412.     ShutdownChessPrograms(message);
  5413.     return;
  5414.     }
  5415.     
  5416.     if ((end_str = strchr(message, '\r')) != NULL)
  5417.       *end_str = NULLCHAR;
  5418.     if ((end_str = strchr(message, '\n')) != NULL)
  5419.       *end_str = NULLCHAR;
  5420.     
  5421.     if (appData.debugMode)
  5422.       fprintf(stderr, "Received from %s: %s\n",
  5423.           fp == fromFirstProgFP ? "first" : "second", message);
  5424.     HandleMachineMove(message, fp);
  5425. }
  5426.  
  5427. void SendSearchDepth(fp)
  5428.      FILE *fp;
  5429. {
  5430.     char message[MSG_SIZ];
  5431.     
  5432.     if (appData.searchDepth <= 0) return;
  5433.     
  5434.     sprintf(message, "depth\n%d\nhelp\n", appData.searchDepth);
  5435.     /* note kludge: "help" command forces gnuchessx to print
  5436.        out something that ends with a newline. */
  5437.     SendToProgram(message, fp);
  5438. }
  5439.  
  5440. void SendTimeRemaining(fp)
  5441.      FILE *fp;
  5442. {
  5443.     char message[MSG_SIZ];
  5444.     long time;
  5445.  
  5446.     if (WhiteOnMove(forwardMostMove))
  5447.       time = whiteTimeRemaining;
  5448.     else
  5449.       time = blackTimeRemaining;
  5450.     
  5451.     if (time <= 0) time = 1000;
  5452.     
  5453.     sprintf(message, "time %d\n", time/10);
  5454.     SendToProgram(message, fp);
  5455. }
  5456.  
  5457.  
  5458. void DisplayMove(moveNumber)
  5459.      int moveNumber;
  5460. {
  5461.     char message[MSG_SIZ];
  5462.     
  5463.     if (moveNumber < 0) {
  5464.     if (moveNumber == forwardMostMove - 1)
  5465.       DisplayMessage(endMessage);
  5466.     else
  5467.       DisplayMessage("");
  5468.     } else {
  5469.     sprintf(message, "%d. %s%s  %s", moveNumber / 2 + 1,
  5470.         WhiteOnMove(moveNumber) ? "" : "... ",
  5471.         parseList[moveNumber],
  5472.         moveNumber == forwardMostMove - 1 ? endMessage : "");
  5473.     DisplayMessage(message);
  5474.     }
  5475. }
  5476.  
  5477. void DisplayMessage(message)
  5478.      char *message;
  5479. {
  5480.     Arg arg;
  5481.     
  5482.     XtSetArg(arg, XtNlabel, message);
  5483.     XtSetValues(messageWidget, &arg, 1);
  5484. }
  5485.  
  5486. void DisplayTitle(title)
  5487.      char *title;
  5488. {
  5489.     Arg arg;
  5490.     
  5491.     XtSetArg(arg, XtNlabel, title);
  5492.     XtSetValues(titleWidget, &arg, 1);
  5493. }
  5494.  
  5495. /*
  5496.  * This routine sends a SIGINT (^C interrupt) to gnuchess to awaken it
  5497.  * if it might be busy thinking on our time.  This normally isn't needed,
  5498.  * but is useful on systems where the FIONREAD ioctl doesn't work (such 
  5499.  * as ESIX), since on those systems the gnuchess feature that lets you 
  5500.  * interrupt its thinking just by typing a command does not work.
  5501.  *
  5502.  * In the future, similar code could be used to stop gnuchess and make
  5503.  * it move immediately when it is thinking about its own move; this could
  5504.  * be useful if we want to make Backward or ForceMoves work while gnuchess
  5505.  * is thinking. --t.mann
  5506.  */
  5507. void Attention(pid)
  5508.      int pid;
  5509. {
  5510. #if defined(ATTENTION) || defined(ESIX) || !defined(FIONREAD)
  5511.     if (appData.noChessProgram || (pid == 0)) return;
  5512.     switch (gameMode) {
  5513.       case MachinePlaysWhite:
  5514.       case MachinePlaysBlack:
  5515.       case TwoMachinesPlay:
  5516.     if (forwardMostMove > backwardMostMove + 1 && maybeThinking) {
  5517.         if (appData.debugMode)
  5518.           fprintf(stderr, "Sending SIGINT to %s\n",
  5519.               pid == firstProgramPID ? "first" : "second");
  5520.         (void) kill(pid, SIGINT); /* stop it thinking */
  5521.     }
  5522.     break;
  5523.     }
  5524. #endif /*ATTENTION*/
  5525. }
  5526.  
  5527. void CheckFlags()
  5528. {
  5529.     if (whiteTimeRemaining <= 0) {
  5530.     if (!whiteFlag) {
  5531.         whiteFlag = True;
  5532.         if (appData.icsActive) {
  5533.         if (appData.autoCallFlag &&
  5534.             ics_mode == IcsPlayingBlack && !blackFlag)
  5535.           SendToICS("flag\n");
  5536.         } else {
  5537.         if (blackFlag)
  5538.           DisplayTitle("Both flags have fallen");
  5539.         else
  5540.           DisplayTitle("White's flag has fallen");
  5541.         }
  5542.     }
  5543.     }
  5544.     if (blackTimeRemaining <= 0) {
  5545.     if (!blackFlag) {
  5546.         blackFlag = True;
  5547.         if (appData.icsActive) {
  5548.         if (appData.autoCallFlag &&
  5549.             ics_mode == IcsPlayingWhite && !whiteFlag)
  5550.           SendToICS("flag\n");
  5551.         } else {
  5552.         if (whiteFlag)
  5553.           DisplayTitle("Both flags have fallen");
  5554.         else
  5555.           DisplayTitle("Black's flag has fallen");
  5556.         }
  5557.     }
  5558.     }
  5559. }
  5560.  
  5561. void CheckTimeControl()
  5562. {
  5563.     if (!appData.clockMode) return;
  5564.     if (appData.icsActive) return;
  5565.     if (forwardMostMove == 0) return;
  5566.     /*
  5567.      * add time to clocks when time control is achieved
  5568.      */
  5569.     if ((forwardMostMove % (appData.movesPerSession * 2)) == 0) {
  5570.     whiteTimeRemaining += timeControl;
  5571.     blackTimeRemaining += timeControl;
  5572.     }
  5573. }
  5574.  
  5575. void DisplayLabels()
  5576. {
  5577.     DisplayTimerLabel(whiteTimerWidget, "White", whiteTimeRemaining);
  5578.     DisplayTimerLabel(blackTimerWidget, "Black", blackTimeRemaining);
  5579. }
  5580.  
  5581. #ifdef HAS_GETTIMEOFDAY
  5582. static struct timeval tickStartTV;
  5583. static int tickLength;
  5584.  
  5585. int PartialTickLength()
  5586. {
  5587.     struct timeval tv;
  5588.     struct timezone tz;
  5589.     int ptl;
  5590.     
  5591.     gettimeofday(&tv, &tz);
  5592.     ptl = ( (tv.tv_sec - tickStartTV.tv_sec)*1000000 +
  5593.        (tv.tv_usec - tickStartTV.tv_usec) + 500 ) / 1000;
  5594.     if (ptl > tickLength) ptl = tickLength;
  5595.     return ptl;
  5596. }
  5597. #else /*!HAS_GETTIMEOFDAY*/
  5598. #define tickLength 1000
  5599. #endif /*HAS_GETTIMEOFDAY*/
  5600.  
  5601. /*
  5602.  * DisplayClocks manages the game clocks.
  5603.  *
  5604.  * In tournament play, black starts the clock and then white makes a move.
  5605.  * We give the human user a slight advantage if he is playing white---the
  5606.  * clocks don't run until he makes his first move, so it takes zero time.
  5607.  * Also, DisplayClocks doesn't account for network lag so it could get
  5608.  * out of sync with GNU Chess's clock -- but then, referees are always right.
  5609.  */
  5610. void DisplayClocks(clock_mode)
  5611.      int clock_mode;
  5612. {
  5613. #ifdef HAS_GETTIMEOFDAY
  5614.     struct timezone tz;
  5615. #endif /*HAS_GETTIMEOFDAY*/
  5616.     long timeRemaining;
  5617.     
  5618.     switch (clock_mode) {
  5619.       case ResetTimers:
  5620.     /* Stop clocks and reset to a fresh time control */
  5621.     if (timerXID != 0) {
  5622.         XtRemoveTimeOut(timerXID);
  5623.         timerXID = 0;
  5624.     }
  5625.     whiteTimeRemaining = timeControl;
  5626.     blackTimeRemaining = timeControl;
  5627.     if (whiteFlag || blackFlag) {
  5628.         DisplayTitle("");
  5629.         whiteFlag = blackFlag = False;
  5630.     }
  5631.     DisplayLabels();
  5632.     break;
  5633.     
  5634.       case DecrementTimers:
  5635.     /* Decrement running clock to next 1-second boundary */
  5636.     timerXID = 0;
  5637.     if (!appData.clockMode) return;
  5638.     
  5639.     if (WhiteOnMove(forwardMostMove)) {
  5640.         timeRemaining = whiteTimeRemaining -= tickLength;
  5641.         DisplayTimerLabel(whiteTimerWidget, "White", whiteTimeRemaining);
  5642.     } else {
  5643.         timeRemaining = blackTimeRemaining -= tickLength;
  5644.         DisplayTimerLabel(blackTimerWidget, "Black", blackTimeRemaining);
  5645.     }
  5646.     
  5647.     CheckFlags();
  5648.     
  5649. #ifdef HAS_GETTIMEOFDAY
  5650.     tickLength = (timeRemaining <= 1000 && timeRemaining > 0) ? 100 : 1000;
  5651.     gettimeofday(&tickStartTV, &tz);
  5652. #endif /*HAS_GETTIMEOFDAY*/
  5653.     timerXID =
  5654.       XtAppAddTimeOut(appContext, tickLength,
  5655.               (XtTimerCallbackProc) DisplayClocks,
  5656.               (XtPointer) DecrementTimers);
  5657.     break;
  5658.     
  5659.       case SwitchTimers:
  5660.     /* A player has just moved, so stop the previously running
  5661.        clock and start the other one. */
  5662.     if (timerXID != 0) {
  5663.         XtRemoveTimeOut(timerXID);
  5664.         timerXID = 0;
  5665. #ifdef HAS_GETTIMEOFDAY
  5666.         if (appData.clockMode) {
  5667.         if (WhiteOnMove(forwardMostMove))
  5668.           blackTimeRemaining -= PartialTickLength();
  5669.         else
  5670.           whiteTimeRemaining -= PartialTickLength();
  5671.         CheckFlags();
  5672.         }
  5673. #endif /*HAS_GETTIMEOFDAY*/
  5674.     }
  5675.     CheckTimeControl();
  5676.     DisplayLabels();
  5677.     if (!appData.clockMode) return;
  5678.     if (gameMode == PauseGame &&
  5679.         (pausePreviousMode == MachinePlaysBlack ||
  5680.          pausePreviousMode == MachinePlaysWhite)) return;
  5681.     
  5682.     timeRemaining = WhiteOnMove(forwardMostMove) ?
  5683.       whiteTimeRemaining : blackTimeRemaining;
  5684.  
  5685. #ifdef HAS_GETTIMEOFDAY
  5686.     tickLength = (timeRemaining <= 1000 && timeRemaining > 0) ?
  5687.       ((timeRemaining-1) % 100) + 1 : ((timeRemaining-1) % 1000) + 1;
  5688.     if (tickLength <= 0) tickLength += 1000;
  5689.     gettimeofday(&tickStartTV, &tz);
  5690. #endif /*HAS_GETTIMEOFDAY*/
  5691.     timerXID =
  5692.       XtAppAddTimeOut(appContext, tickLength,
  5693.               (XtTimerCallbackProc) DisplayClocks,
  5694.               (XtPointer) DecrementTimers);
  5695.     break;
  5696.     
  5697.       case ReDisplayTimers:
  5698.     /* Display current clock values */
  5699.     DisplayLabels();
  5700.     break;
  5701.     
  5702.       case StopTimers:
  5703.     /* Stop both clocks */
  5704.     if (timerXID == 0) return;
  5705.     XtRemoveTimeOut(timerXID);
  5706.     timerXID = 0;
  5707.     if (!appData.clockMode) return;
  5708. #ifdef HAS_GETTIMEOFDAY
  5709.     if (WhiteOnMove(forwardMostMove))
  5710.       whiteTimeRemaining -= PartialTickLength();
  5711.     else
  5712.       blackTimeRemaining -= PartialTickLength();
  5713.     CheckFlags();
  5714.     DisplayLabels();
  5715. #endif /*HAS_GETTIMEOFDAY*/
  5716.     break;
  5717.     
  5718.       case StartTimers:
  5719.     /* Start clock of player on move, if not already running. */
  5720.     DisplayLabels();
  5721.     if (!appData.clockMode) return;
  5722.     if (timerXID != 0) return;
  5723.     
  5724.     timeRemaining = WhiteOnMove(forwardMostMove) ?
  5725.       whiteTimeRemaining : blackTimeRemaining;
  5726.  
  5727. #ifdef HAS_GETTIMEOFDAY
  5728.     tickLength = (timeRemaining <= 1000 && timeRemaining > 0) ?
  5729.       ((timeRemaining-1) % 100) + 1 : ((timeRemaining-1) % 1000) + 1;
  5730.     if (tickLength <= 0) tickLength += 1000;
  5731.     gettimeofday(&tickStartTV, &tz);
  5732. #endif /*HAS_GETTIMEOFDAY*/
  5733.     timerXID =
  5734.       XtAppAddTimeOut(appContext, tickLength,
  5735.               (XtTimerCallbackProc) DisplayClocks,
  5736.               (XtPointer)DecrementTimers);
  5737.     break;
  5738.     }
  5739. }
  5740.  
  5741. void DisplayTimerLabel(w, color, timer)
  5742.      Widget w;
  5743.      char *color;
  5744.      long timer;
  5745. {
  5746.     char buf[MSG_SIZ];
  5747.     Arg args[3];
  5748.     
  5749.     if (appData.clockMode) {
  5750.     sprintf(buf, "%s: %s", color, TimeString(timer));
  5751.     XtSetArg(args[0], XtNlabel, buf);
  5752.     } else {
  5753.     XtSetArg(args[0], XtNlabel, color);
  5754.     }
  5755.     
  5756.     if (((color[0] == 'B') && WhiteOnMove(currentMove))
  5757.     || ((color[0] == 'W') && !WhiteOnMove(currentMove))) {
  5758.     XtSetArg(args[1], XtNbackground, timerForegroundPixel);
  5759.     XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
  5760.     } else {
  5761.     XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
  5762.     XtSetArg(args[2], XtNforeground, timerForegroundPixel);
  5763.     }
  5764.     
  5765.     XtSetValues(w, args, 3);
  5766. }
  5767.  
  5768. char *TimeString(tm)
  5769.      long tm;
  5770. {
  5771.     int second, minute, hour, day;
  5772.     char *sign = "";
  5773.     static char buf[32];
  5774.     
  5775.     if (tm > 0 && tm <= 900) {
  5776.     /* convert milliseconds to tenths, rounding up */
  5777.     sprintf(buf, " 0.%1d ", (tm+99)/100);
  5778.     return buf;
  5779.     }
  5780.  
  5781.     /* convert milliseconds to seconds, rounding up */
  5782.     tm = (tm + 999) / 1000; 
  5783.  
  5784.     if (tm < 0) {
  5785.     sign = "-";
  5786.     tm = -tm;
  5787.     }
  5788.     
  5789.     if (tm >= (60 * 60 * 24)) {
  5790.     day = (int) (tm / (60 * 60 * 24));
  5791.     tm -= day * 60 * 60 * 24;
  5792.     } else {
  5793.     day = 0;
  5794.     }
  5795.     
  5796.     if (tm >= (60 * 60)) {
  5797.     hour = (int) (tm / (60 * 60));
  5798.     tm -= hour * 60 * 60;
  5799.     } else {
  5800.     hour = 0;
  5801.     }
  5802.     
  5803.     if (tm >= 60) {
  5804.     minute = (int) (tm / 60);
  5805.     tm -= minute * 60;
  5806.     } else {
  5807.     minute = 0;
  5808.     }
  5809.     
  5810.     second = tm % 60;
  5811.     
  5812.     if (day > 0)
  5813.       sprintf(buf, " %s%d:%02d:%02d:%02d ", sign, day, hour, minute, second);
  5814.     else if (hour > 0)
  5815.       sprintf(buf, " %s%d:%02d:%02d ", sign, hour, minute, second);
  5816.     else
  5817.       sprintf(buf, " %s%2d:%02d ", sign, minute, second);
  5818.     
  5819.     return buf;
  5820. }
  5821.  
  5822. void Usage()
  5823. {
  5824.     fprintf(stderr, "Usage: %s\n", programName);
  5825.     fprintf(stderr, "\tstandard Xt options\n");
  5826.     fprintf(stderr, "\t-iconic\n");
  5827.     fprintf(stderr, "\t-tc or -timeControl minutes[:seconds]\n");
  5828.     fprintf(stderr, "\t-mps or -movesPerSession moves\n");
  5829.     fprintf(stderr, "\t-st or -searchTime minutes[:seconds]\n");
  5830.     fprintf(stderr, "\t-sd or -searchDepth number\n");
  5831.     fprintf(stderr, "\t-clock or -clockMode (True | False)\n");
  5832.     fprintf(stderr, "\t-autoflag or -autoCallFlag (True | False)\n");
  5833.     fprintf(stderr, "\t-td or -timeDelay seconds\n");
  5834.     fprintf(stderr, "\t-ics or -internetChessServerMode (True | False)\n");
  5835.     fprintf(stderr, "\t-icshost or -internetChessServerHost host_name\n");
  5836.     fprintf(stderr, "\t-icsport or -internetChessServerPort port_number\n");
  5837.     fprintf(stderr, "\t-ncp or -noChessProgram (True | False)\n");
  5838.     fprintf(stderr, "\t-fcp or -firstChessProgram program_name\n");
  5839.     fprintf(stderr, "\t-scp or -secondChessProgram program_name\n");
  5840.     fprintf(stderr, "\t-fh or -firstHost host_name\n");
  5841.     fprintf(stderr, "\t-sh or -secondHost host_name\n");
  5842.     fprintf(stderr, "\t-rsh or -remoteShell shell_name\n");
  5843.     fprintf(stderr,
  5844.         "\t-mm or -matchMode (False | Init | Position | Opening)\n");
  5845.     fprintf(stderr, "\t-lgf or -loadGameFile file_name\n");
  5846.     fprintf(stderr, "\t-sgf or -saveGameFile file_name\n");
  5847.     fprintf(stderr, "\t-autosave or -autoSaveGames (True | False)\n");
  5848.     fprintf(stderr, "\t-lpf or -loadPositionFile file_name\n");
  5849.     fprintf(stderr, "\t-spf or -savePositionFile file_name\n");
  5850.     fprintf(stderr, "\t-bell or -ringBellAfterMoves (True | False)\n");
  5851.     fprintf(stderr, "\t-size or -boardSize (Large | Medium | Small)\n");
  5852.     fprintf(stderr, "\t-coords or -showCoords (True | False)\n");
  5853.     fprintf(stderr, "\t-mono or -monoMode (True | False)\n");
  5854.     fprintf(stderr, "\t-debug or -debugMode (True | False)\n");
  5855.     exit(2);
  5856. }
  5857.  
  5858. /*
  5859.  * This is necessary because some C libraries aren't ANSI C compliant yet.
  5860.  */
  5861. char *StrStr(string, match)
  5862.      char *string, *match;
  5863. {
  5864.     int i, length;
  5865.     
  5866.     length = strlen(match);
  5867.     
  5868.     for (i = strlen(string) - length; i >= 0; i--, string++)
  5869.       if (!strncmp(match, string, (size_t) length))
  5870.     return string;
  5871.     
  5872.     return NULL;
  5873. }
  5874.  
  5875. int StrCaseCmp(s1, s2)
  5876.      char *s1, *s2;
  5877. {
  5878.     char c1, c2;
  5879.     
  5880.     for (;;) {
  5881.     c1 = ToLower(*s1++);
  5882.     c2 = ToLower(*s2++);
  5883.     if (c1 > c2) return 1;
  5884.     if (c1 < c2) return -1;
  5885.     if (c1 == NULLCHAR) return 0;
  5886.     }
  5887. }
  5888.  
  5889.  
  5890. int ToLower(c)
  5891.      int c;
  5892. {
  5893.     return isupper(c) ? tolower(c) : c;
  5894. }
  5895.  
  5896.  
  5897. int ToUpper(c)
  5898.      int c;
  5899. {
  5900.     return islower(c) ? toupper(c) : c;
  5901. }
  5902.  
  5903.  
  5904. #if defined(SYSTEM_FIVE) || defined(SYSV)
  5905. #ifdef    IRIX /*??*/
  5906. char *PseudoTTY(ptyv)
  5907.      int *ptyv;
  5908. {
  5909.     char *line;
  5910.     
  5911.     line = (char *)_getpty(ptyv, O_RDWR, 0600, 0);
  5912.     if (0 == line)
  5913.       return NULL;
  5914.     return line;
  5915. }
  5916. #else    /*!IRIX*/
  5917. char *PseudoTTY(ptyv)
  5918.      int *ptyv;
  5919. {
  5920. #ifdef SVR4
  5921.     char *ptsname(), *ptss;
  5922.     
  5923.     *ptyv = open("/dev/ptmx", O_RDWR);
  5924.     if (*ptyv > 0 ) {
  5925.     if (grantpt(*ptyv) == -1)
  5926.       return NULL;
  5927.     if (unlockpt(*ptyv) == -1)
  5928.       return NULL;
  5929.     if (!(ptss = ptsname(*ptyv)))
  5930.       return NULL;
  5931.     strncpy(ptyname, ptss, sizeof(ptyname));
  5932.     ptyname[sizeof(ptyname) -1] = 0;
  5933.     return ptyname;
  5934.     }
  5935.     
  5936. #else /* !SVR4 */
  5937.     struct stat stb;
  5938.     int c, i;
  5939.     
  5940.     for (c = 'p'; c <= 'z'; c++)
  5941. #if defined(HPUX) || defined(hpux)
  5942.       for (i = 0; i < 15 /*??*/; i++) {
  5943.       sprintf(ptyname, "/dev/ptym/pty%c%x", c, i);
  5944. #else /* !HPUX */
  5945.       for (i = 0; i < 16; i++) {
  5946. #ifdef RTU
  5947.       sprintf(ptyname, "/dev/pty%x", i);
  5948. #else /* !RTU */
  5949.       sprintf(ptyname, "/dev/pty%c%x", c, i);
  5950. #endif /* RTU */
  5951. #endif /* HPUX */
  5952.           
  5953. #ifdef IRIS
  5954.       *ptyv = open("/dev/ptc", O_RDWR, 0);
  5955.       if (*ptyv < 0)
  5956.         return NULL;
  5957.       if (fstat(*ptyv, &stb) < 0)
  5958.         return NULL;
  5959. #else /* !IRIS */
  5960.       if (stat(ptyname, &stb) < 0)
  5961.         return NULL;
  5962.       *ptyv = open(ptyname, O_RDWR, 0);
  5963. #endif /* IRIS */
  5964.           
  5965.       if (*ptyv >= 0) {
  5966. #if defined(HPUX) || defined(hpux)
  5967.           sprintf(ptyname, "/dev/pty/tty%c%x", c, i);
  5968. #else /* !HPUX */
  5969. #ifdef RTU
  5970.           sprintf(ptyname, "/dev/ttyp%x", i);
  5971. #else /* !RTU */
  5972. #ifdef IRIS
  5973.           sprintf(ptyname, "/dev/ttyq%d",
  5974.               minor(stb.st_rdev));
  5975. #else /* !IRIS, !RTU, !HPUX */
  5976.           sprintf(ptyname, "/dev/tty%c%x", c, i);
  5977. #endif /* IRIS */
  5978. #endif /* RTU */
  5979. #endif /* HPUX */
  5980.           
  5981. #ifndef    UNIPLUS
  5982.           if (access(ptyname, 6) != 0) {
  5983.           close(*ptyv);
  5984. #ifdef IRIS
  5985.           return NULL;
  5986. #else /* !IRIS */
  5987.           continue;
  5988. #endif /* IRIS */
  5989.           }
  5990. #endif /* !UNIPLUS */
  5991.           
  5992. #ifdef IBMRTAIX
  5993.           signal(SIGHUP, SIG_IGN);
  5994. #endif /* IBMRTAIX */
  5995.           return ptyname;
  5996.       }
  5997.       }
  5998.       
  5999. #endif
  6000.     return NULL;
  6001. }
  6002. #endif /*IRIX*/
  6003.     
  6004. #else /* !SYSTEM_FIVE */
  6005.     
  6006. void CatchPipeSignal(dummy)
  6007.   int dummy;
  6008. {
  6009.     char message[MSG_SIZ];
  6010.     char *number, *name;
  6011.     
  6012.     if (lastMsgFP == toFirstProgFP) {
  6013.     number = "first";
  6014.     name = appData.firstChessProgram;
  6015.     } else if (lastMsgFP == toSecondProgFP) {
  6016.     number = "second";
  6017.     name = appData.secondChessProgram;
  6018.     } else {
  6019.     return;
  6020.     }
  6021.  
  6022.     sprintf(message,
  6023.         "%s chess program (%s) exited unexpectedly (pipe signal)",
  6024.         number, name);
  6025.     fprintf(stderr, "%s: %s\n", programName, message);
  6026.     ShutdownChessPrograms(message);
  6027.     return;
  6028. }
  6029. #endif
  6030.